Skip to content

Conversation

@ElonPark
Copy link
Member

@ElonPark ElonPark commented Jul 28, 2025

Background (Required)

  • When decoding JSON data in Swift, property wrappers like @DefaultCodable and @PolymorphicValue can encounter decoding errors that are
    silently ignored, making it difficult to debug issues in production
  • This PR implements resilient decoding functionality that captures and reports decoding errors while still providing fallback values,
    allowing developers to monitor and debug decoding issues without breaking the application

Changes

  • Added resilient decoding infrastructure with ResilientDecodingOutcome and error reporting system
  • Implemented ProjectedValue support for all BetterCodable property wrappers to expose decoding errors
  • Implemented ProjectedValue support for all PolymorphicCodable property wrappers to expose decoding errors
  • Added comprehensive error reporting for array and dictionary decoding failures
  • Reorganized test files into feature-specific directories (Enum, Value) for better maintainability
  • Fixed compiler warnings in DefaultCodable implementation

Testing Methods

  • Run all tests with swift test to ensure existing functionality remains intact
  • Added comprehensive resilient decoding tests for each property wrapper
  • Test files ending with *ResilientTests.swift verify error capture and reporting functionality
  • Each test validates that decoding errors are properly captured in the projected value while still providing the expected fallback
    behavior

Review Notes

  • The resilient decoding feature is backward compatible - existing code will continue to work without modification
  • Developers can opt-in to error monitoring by accessing the projected value (e.g., $propertyName) to check for decoding errors
  • The implementation follows a consistent pattern across all property wrappers for maintainability
  • Error messages include detailed context about what failed and why, making debugging easier

Summary by CodeRabbit

  • New Features
    • Added detailed decoding outcome tracking to all resilient property wrappers, exposing results and errors via debug-only projected properties.
    • Enhanced JSONDecoder integration to enable structured error reporting and diagnostics during decoding.
    • Introduced per-element and per-key error tracking for polymorphic and collection wrappers, improving resilience and observability.
  • Bug Fixes
    • Improved decoding robustness by gracefully handling missing keys, null values, unknown enum cases, and invalid elements with accurate outcome reporting.
  • Tests
    • Added comprehensive test suites covering resilient decoding, error recovery, outcome inspection, and error reporting for all property wrappers and polymorphic types.
  • Documentation
    • Updated README acknowledgements to credit external open-source inspiration.
  • Style
    • Refined test comments for conciseness and clarity.

These updates enhance reliability, diagnostics, and developer insight into decoding workflows across the framework.

- ThirdPartyLicenses/ResilientDecoding/LICENSE 파일 추가
- README.md에 ResilientDecoding acknowledgement 추가
@ElonPark ElonPark self-assigned this Jul 28, 2025
@ElonPark ElonPark added the Feature New feature or request label Jul 28, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 28, 2025

📝 Walkthrough

Walkthrough

This update introduces resilient decoding outcome tracking and error reporting across a wide range of property wrappers and decoding strategies in the KarrotCodableKit. It adds new types for outcome introspection, enhances error reporting infrastructure, and updates decoding logic to capture and expose detailed decoding results. Comprehensive test suites validate these behaviors for arrays, dictionaries, polymorphic types, and default value wrappers.

Changes

Cohort / File(s) Change Summary
Resilient Decoding Core Infrastructure
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift, Sources/KarrotCodableKit/Resilient/ErrorReporting.swift, Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift, Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift
Introduces core types and mechanisms for resilient decoding outcomes, error reporting, and hierarchical error aggregation. Adds debug-friendly error digest and reporting APIs.
Resilient Projected Value Types
Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift
Adds projected value types and protocols for exposing decoding outcomes and errors from property wrappers, supporting dynamic member lookup for array and dictionary errors.
DataValue Property Wrapper
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift
Adds outcome tracking, debug-only projected value, and explicit Equatable/Hashable conformances; updates decoding to report and record errors.
DateValue & OptionalDateValue Wrappers
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift, Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift
Adds outcome tracking, debug-only projected value, explicit Equatable/Hashable, and improved error reporting during decoding for both wrappers.
DefaultCodable & Strategy Extensions
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
Adds outcome tracking, error reporting, debug-only diagnostics, explicit Equatable/Hashable, and improved handling for RawRepresentable defaults with frozen/non-frozen support.
LosslessArray & LosslessValue Wrappers
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift, Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift
Adds outcome tracking, debug-only projected value, explicit Equatable/Hashable, and per-element error reporting for arrays.
LossyArray & LossyDictionary Wrappers
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift, Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift
Adds outcome tracking, debug-only projected value, explicit Equatable/Hashable, and detailed error reporting for lossy decoding of arrays and dictionaries.
Polymorphic Value Wrappers
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift
Adds outcome tracking, debug-only projected value, explicit Equatable/Hashable, and improved error reporting for all polymorphic property wrappers.
Polymorphic Decoding Container Extensions
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift, Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift
Updates decoding logic to explicitly handle key presence, null values, error reporting, and outcome assignment for all relevant polymorphic container extensions.
Documentation
README.md
Adds an acknowledgement for "ResilientDecodingOutcome" as adapted from Airbnb's project.
Test Suites: Resilient Decoding
Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift, Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift, Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift, Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift, Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift, Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift, Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
Adds comprehensive test suites covering resilient decoding, error reporting, and outcome introspection for all major property wrappers and strategies.
Test Suites: Polymorphic Wrappers
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift
Adds new test suites for resilient and lossy decoding of polymorphic arrays, optionals, and values, verifying outcome and error reporting in various scenarios.
Test Comment Cleanup
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift, Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift
Refines test comments for clarity and consistency; no logic changes.
Result Extension
Sources/KarrotCodableKit/BetterCodable/Extenstoins/Result+Extension.swift
Adds convenient package-level computed properties on Result for success/failure checks and value extraction.

Sequence Diagram(s)

sequenceDiagram
    participant JSONDecoder
    participant PropertyWrapper
    participant ErrorReporter
    participant User

    JSONDecoder->>PropertyWrapper: decode(value, from: data)
    PropertyWrapper->>PropertyWrapper: try decode value
    alt Success
        PropertyWrapper->>PropertyWrapper: set outcome = .decodedSuccessfully
    else Key missing
        PropertyWrapper->>PropertyWrapper: set outcome = .keyNotFound
    else Value is nil
        PropertyWrapper->>PropertyWrapper: set outcome = .valueWasNil
    else Decoding error
        PropertyWrapper->>ErrorReporter: reportError(error)
        PropertyWrapper->>PropertyWrapper: set outcome = .recoveredFrom(error, wasReported: true)
    end
    PropertyWrapper-->>User: expose value + projected outcome (in debug)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • This change is extensive: it introduces new infrastructure, touches many property wrappers, revises decoding logic, and adds comprehensive new test suites. Reviewers should allocate time for careful inspection of error handling, outcome propagation, and test coverage.

Suggested labels

Improvement, Docs


Feedback & Suggestions:

  • The introduction of resilient decoding outcomes and error reporting is well-structured and consistent across wrappers, improving debuggability.
  • The debug-only projected value pattern is a practical approach to expose detailed decoding outcomes without impacting release builds; consider documenting its usage and limitations clearly for future maintainers.
  • The comprehensive test suites cover a wide range of scenarios, but it would be beneficial to verify coverage of deeply nested containers and complex error hierarchies to ensure robustness.
  • Adding usage examples in the documentation demonstrating how consumers can leverage the new error reporting and outcome APIs would enhance developer experience.
  • Review the conditional compilation usage to maintain lean release builds while preserving rich diagnostics in debug builds.
  • Overall, this is a significant step forward in resilience and observability of decoding operations. Great job!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/elon/add-resilient-decoding

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @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
Contributor

@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: 29

🔭 Outside diff range comments (3)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1)

160-162: Fix typo in the decodable type name – tests won’t compile as-is
OptionalLossyAarrayDummyDecodableResponse (double “Aa”) does not match the previously-used OptionalLossyArrayDummyDecodableResponse. Unless a differently-spelled type really exists, this will be a hard compile-time failure.

-      OptionalLossyAarrayDummyDecodableResponse.self,
+      OptionalLossyArrayDummyDecodableResponse.self,
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

50-62: Incomplete resilient decoding implementation for array elements

The current implementation only tracks overall success but doesn't capture individual element decoding failures. Based on the pattern in LossyArray.swift, this should track element-level outcomes for proper resilient decoding.

Consider implementing element-level error tracking:

 extension PolymorphicArrayValue: Decodable {
   public init(from decoder: Decoder) throws {
     var container = try decoder.unkeyedContainer()
 
     var elements = [PolymorphicType.ExpectedType]()
+    #if DEBUG
+    var elementOutcomes: [ResilientDecodingOutcome] = []
+    #endif
+    
     while !container.isAtEnd {
-      let value = try container.decode(PolymorphicValue<PolymorphicType>.self).wrappedValue
-      elements.append(value)
+      let polymorphicValue = try container.decode(PolymorphicValue<PolymorphicType>.self)
+      elements.append(polymorphicValue.wrappedValue)
+      #if DEBUG
+      elementOutcomes.append(polymorphicValue.outcome)
+      #endif
     }
 
     self.wrappedValue = elements
-    self.outcome = .decodedSuccessfully
+    #if DEBUG
+    // Check if any element had decoding issues
+    let hasFailures = elementOutcomes.contains { outcome in
+      if case .recoveredFrom = outcome { return true }
+      return false
+    }
+    self.outcome = hasFailures ? .recoveredFrom(ArrayDecodingError(elementOutcomes: elementOutcomes), wasReported: false) : .decodedSuccessfully
+    #else
+    self.outcome = .decodedSuccessfully
+    #endif
   }
 }
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (1)

157-170: Consider performance impact of double decoding [MEDIUM]

The extractKeys method decodes the entire dictionary structure just to extract keys in their original form. For large dictionaries, this could be inefficient. Consider if there's a way to preserve the original keys during the main decoding pass.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f022148 and 98b830a.

⛔ Files ignored due to path filters (1)
  • ThirdPartyLicenses/ResilientDecoding/LICENSE is excluded by !**/ThirdPartyLicenses/**
📒 Files selected for processing (44)
  • README.md (1 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (6 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (3 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (4 hunks)
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (3 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (4 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (3 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Tests/KarrotCodableKitTests/**

⚙️ CodeRabbit Configuration File

Tests/KarrotCodableKitTests/**: You are a senior Swift/iOS engineer reviewing runtime functionality tests for KarrotCodableKit's Codable extensions.

1. Runtime Behavior Testing [HIGH]

  • Verify JSON encoding/decoding scenarios cover real-world use cases
  • Check polymorphic type resolution testing across different strategies
  • Assess property wrapper testing (@DATEvalue, @DefaultFalse, etc.)
  • Validate error handling and edge case coverage

2. Test Data Quality [HIGH]

  • Review test JSON structures for realistic complexity
  • Check that TestDoubles provide comprehensive mock scenarios
  • Verify test data covers various data types and edge cases
  • Assess polymorphic test data represents actual usage patterns

3. Integration Testing [MEDIUM]

  • Check integration between macro-generated code and runtime functionality
  • Verify end-to-end scenarios combining multiple KarrotCodableKit features
  • Assess performance testing for large data structures
  • Review memory usage testing for complex polymorphic scenarios

Review Focus

  • Prioritize real-world usage scenario coverage
  • Focus on Codable correctness and performance implications
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift
Sources/KarrotCodableKit/**

⚙️ CodeRabbit Configuration File

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift
*

⚙️ CodeRabbit Configuration File

*: You are a senior Swift/iOS engineer with 15+ years of experience reviewing KarrotCodableKit code changes.

Review Priorities [HIGH]

  • Code quality: memory leaks, error handling, readability (100 chars/line)
  • Architecture: SOLID principles, design patterns, DRY/KISS
  • Swift best practices: modern features, memory management, performance

Communication

  • Mark priority: [HIGH], [MEDIUM], [LOW]
  • Provide specific code examples
  • Focus on high-impact improvements over style

Files:

  • README.md
**/*.md

⚙️ CodeRabbit Configuration File

**/*.md: You are a senior technical writer reviewing documentation for KarrotCodableKit, a Swift Codable extension library.

1. Technical Accuracy [HIGH]

  • Verify code examples compile and work correctly
  • Check that API documentation matches actual implementation
  • Validate macro usage examples demonstrate proper syntax
  • Assess JSON examples are well-formed and realistic

2. Documentation Completeness [HIGH]

  • Review coverage of all major features (CustomCodable, PolymorphicCodable, AnyCodable, BetterCodable)
  • Check that complex concepts like polymorphic type resolution are well explained
  • Verify installation and setup instructions are current
  • Assess troubleshooting and error handling guidance

3. User Experience [MEDIUM]

  • Evaluate documentation structure and navigation clarity
  • Check that examples progress from simple to complex appropriately
  • Verify code snippets include necessary imports and context
  • Assess whether documentation answers common user questions

Review Focus

  • Prioritize accuracy and completeness over style
  • Focus on developer experience and practical usage scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • README.md
🧬 Code Graph Analysis (17)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (1)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
  • decode (12-29)
Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (2)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)
  • decode (89-94)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (3)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (2)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
  • decodeIfPresent (31-48)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (5)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (2)
  • lhs (70-72)
  • hash (76-78)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2)
  • lhs (84-86)
  • hash (90-92)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (2)
  • lhs (107-109)
  • hash (113-115)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • errors (86-95)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • errors (86-95)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (3)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
  • arrayDecodingError (30-41)
Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)
  • dictionaryDecodingError (30-41)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (3)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Tests/KarrotCodableKitTests/PolymorphicCodable/LossyOptionalPolymorphicValueTests.swift (2)
  • LossyOptionalPolymorphicValueTests (13-98)
  • LossyOptionalPolymorphicValueTests (100-125)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (3)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (5)
  • decode (120-149)
  • decode (157-208)
  • decode (215-278)
  • lhs (99-101)
  • hash (105-107)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (2)
  • lhs (86-88)
  • hash (92-94)
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (8)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (2)
  • decode (89-94)
  • lhs (75-77)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (2)
  • decode (190-200)
  • lhs (180-182)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (4)
  • decode (120-149)
  • decode (157-208)
  • decode (215-278)
  • lhs (99-101)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)
  • lhs (70-72)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (1)
  • lhs (86-88)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (1)
  • lhs (84-86)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)
  • lhs (107-109)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (1)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)
  • decodeIfPresent (48-69)
🪛 SwiftLint (0.57.0)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift

[Error] 91-91: Force casts should be avoided

(force_cast)


[Error] 93-93: Force casts should be avoided

(force_cast)


[Error] 100-100: Force casts should be avoided

(force_cast)


[Error] 115-115: Force casts should be avoided

(force_cast)


[Error] 117-117: Force casts should be avoided

(force_cast)


[Error] 124-124: Force casts should be avoided

(force_cast)

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift

[Warning] 6-6: Type name 'DefaultEmptyPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift

[Warning] 6-6: Type name 'LossyOptionalPolymorphicValueResilientTests' should be between 3 and 40 characters long

(type_name)

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift

[Warning] 16-16: Type name 'ResilientOptionalPolymorphicArrayDummyResponse' should be between 3 and 40 characters long

(type_name)


[Warning] 30-30: Type name 'OptionalPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

🪛 markdownlint-cli2 (0.17.2)
README.md

280-280: Files should end with a single newline character

(MD047, single-trailing-newline)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build and Test (16.1, release)
  • GitHub Check: Check Macro Compatibility
🔇 Additional comments (101)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (2)

147-155: Verify JSON key vs. property name mismatch
The sample JSON uses the key "notice3" (singular) while the assertions reference result.notices3 (plural). If the model doesn’t define a custom CodingKeys mapping those two spellings, decoding will silently drop the field and always yield an empty array, masking potential issues.

Please double-check the CodingKeys of OptionalLossyArrayDummyDecodableResponse (or its renamed counterpart) and, if necessary, align the JSON fixture:

-      "notice3" : null
+      "notices3" : null     // if no custom CodingKey mapping exists

164-164: Minor comment wording fix looks good
The change from “thens” to “then” improves clarity. No further action needed.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1)

120-120: Good catch on the typo fix!

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (1)

116-116: Consistent comment formatting applied.

The removal of colons from test structure markers creates a cleaner format across all test methods.

Also applies to: 133-133, 136-136, 163-163

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (2)

16-21: Clean implementation with proper outcome tracking.

The method now properly differentiates between missing keys and other decoding scenarios, providing valuable debugging information through the outcome property.


23-46: Well-structured implementation with clear error handling semantics.

The method now provides explicit handling for all edge cases:

  • Missing keys return nil
  • Null values return wrapped nil with .valueWasNil outcome
  • Decoding errors are propagated as expected for OptionalPolymorphicValue

The comment explaining the error throwing behavior is helpful for future maintainers.

Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)

10-31: Excellent design for debug-mode outcome tracking.

The enum design provides rich debugging information while maintaining Sendable conformance for concurrency safety. The custom Equatable implementation is particularly well-thought-out, comparing only the semantically relevant parts (outcome type and reporting status) while ignoring the error values themselves.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (3)

18-45: Test correctly validates successful decoding scenario [MEDIUM]

The test properly verifies both the functional behavior and the resilient decoding outcome tracking for successful decoding cases.


77-96: Null handling test is appropriate [LOW]

The test correctly verifies that non-optional polymorphic values cannot be decoded from null JSON values.


98-140: Error reporting test provides good coverage [HIGH]

The test effectively validates that decoding errors are properly reported through the resilient decoding error reporter and can be retrieved via flushReportedErrors(). The debug print statement is helpful for test debugging.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (5)

11-30: Empty array test covers important edge case [MEDIUM]

Good coverage of the empty array scenario, correctly verifying that empty arrays decode successfully without errors.


32-74: Comprehensive test for polymorphic array decoding [HIGH]

Excellent test coverage with multiple polymorphic types in a single array. The test properly validates both the decoded values and the resilient decoding outcome.


76-100: Test correctly validates strict array decoding behavior [HIGH]

The test properly verifies that PolymorphicArrayValue fails entirely when any element cannot be decoded, which is the expected behavior for non-lossy array decoding.


102-130: Error handling tests provide good coverage [MEDIUM]

Both tests correctly validate error scenarios - missing required key and type mismatch. These are essential test cases for robust decoding.


132-163: Error reporting test validates integration [HIGH]

The test effectively verifies that array element decoding errors are properly captured by the error reporter and can be retrieved for debugging purposes.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (3)

21-36: Outcome tracking implementation follows established patterns [HIGH]

The addition of outcome tracking and the DEBUG-only projected value aligns well with the resilient decoding framework implemented across other property wrappers.


85-95: Standard protocol conformances [LOW]

The Equatable and Hashable implementations correctly delegate to the wrapped array, following the established pattern across the codebase.


46-76: Logic error in outcome determination [HIGH]

The condition elements.count == results.count on line 67 will always be true since both arrays are updated in tandem within the loop. This means failed decodings will incorrectly report .decodedSuccessfully.

The logic should check if any decoding failures occurred:

-    if elements.count == results.count {
+    if results.allSatisfy({ if case .success = $0 { return true } else { return false } }) {
       self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
     } else {
       let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
       self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
     }

Alternatively, track whether any errors occurred with a boolean flag during the loop.

Likely an incorrect or invalid review comment.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (3)

27-42: Outcome tracking properly integrated [HIGH]

The resilient decoding outcome tracking implementation is consistent with other property wrappers in the codebase.


52-61: Decoding error handling properly implemented [HIGH]

The do-catch structure correctly captures decoding success/failure states and integrates with the error reporting system.


63-73: Protocol conformances follow established patterns [LOW]

The Equatable and Hashable implementations correctly delegate to the wrapped value.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2)

33-50: Outcome tracking follows established patterns [HIGH]

The implementation properly integrates resilient decoding outcome tracking with the existing lossless value functionality.


83-86: Good use of Self type alias [LOW]

The simplified equality operator using Self improves readability.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

74-84: LGTM! Standard conformance implementations

The Equatable and Hashable conformances correctly compare and hash based on the wrapped values, consistent with other property wrappers in the codebase.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)

12-21: Well-structured resilient decoding implementation

The methods properly handle all decoding scenarios with appropriate outcome tracking:

  • Missing keys → .keyNotFound
  • Null values → .valueWasNil
  • Successful decoding → .decodedSuccessfully
  • Decoding errors → .recoveredFrom with error reporting

Also applies to: 23-48

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (2)

40-96: Excellent resilient array decoding implementation

The decoding logic properly:

  • Handles null values with .valueWasNil outcome
  • Uses superDecoder() for element isolation
  • Tracks individual element successes/failures
  • Reports errors appropriately
  • Provides detailed outcomes in DEBUG builds

106-116: LGTM! Standard conformance implementations

The Equatable and Hashable conformances follow the established pattern across property wrappers.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1)

1-117: Comprehensive test coverage for resilient decoding

Excellent test suite that covers all decoding scenarios:

  • ✅ Null value handling with .valueWasNil outcome
  • ✅ Missing key handling with .keyNotFound outcome
  • ✅ Successful decoding verification
  • ✅ Error cases for unknown types and invalid JSON
  • ✅ Proper DEBUG-only outcome assertions
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (1)

71-71: Good use of Self in equality operator

Using Self instead of the explicit type name improves maintainability and follows Swift best practices.

Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (5)

27-38: Well-implemented resilient decoding outcome tracking! [HIGH]

The addition of the outcome property and internal initializer follows the established pattern across the codebase perfectly. The default of .decodedSuccessfully for programmatic initialization is sensible.


40-42: Excellent debug-only projected value implementation! [HIGH]

The conditional compilation ensures zero overhead in production builds while providing valuable debugging capabilities during development.


47-60: Robust error handling with proper reporting! [HIGH]

The nested do-catch structure correctly handles both string decoding and strategy application errors. The error reporting via decoder.reportError(error) maintains excellent diagnostics.


69-73: Correct Equatable implementation! [MEDIUM]

Comparing only the wrappedValue is the right approach - the outcome is internal diagnostic information and shouldn't affect equality semantics.


75-79: Consistent Hashable implementation! [MEDIUM]

Hashing only the wrappedValue maintains consistency with the equality semantics and follows the established pattern across property wrappers.

Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (3)

62-79: Excellent resilient decoding implementation! [HIGH]

The error handling and reporting logic is well-implemented. The fallback to empty array behavior is preserved while adding comprehensive error diagnostics via decoder.reportError(error).


91-95: Correct Equatable implementation! [MEDIUM]

The implementation correctly compares only the wrappedValue, which is consistent with the pattern established across other property wrappers in the codebase.


97-101: Consistent Hashable implementation! [MEDIUM]

Hashing only the wrappedValue maintains consistency with the equality semantics and follows the established pattern.

Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (3)

28-43: Consistent resilient decoding implementation! [HIGH]

The outcome tracking and debug-only projected value follow the established pattern perfectly across the property wrapper ecosystem.


50-62: Well-structured error handling enhancement! [HIGH]

The nested do-catch structure properly isolates strategy decoding errors while maintaining the existing nil value handling logic. Error reporting via decoder.reportError(error) is correctly implemented.


75-75: Nice code style improvement! [LOW]

Simplifying the generic type specification to use Self improves readability while maintaining the same functionality.

Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (2)

10-28: Well-designed error aggregation structure! [HIGH]

The DictionaryDecodingError provides excellent structured error information with efficient error extraction. The use of Result<Value, Error> for per-key outcomes is a clean design choice.


30-42: Robust error extraction logic! [HIGH]

The method correctly handles all ResilientDecodingOutcome cases and properly extracts dictionary-specific errors. The assert for unreported errors is a good debugging aid.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1)

11-165: Excellent test coverage for resilient decoding behavior [HIGH]

The test suite provides comprehensive coverage of all resilient decoding scenarios including edge cases, error conditions, and integration with the error reporting system. The proper use of conditional compilation ensures debug-only features are tested appropriately without affecting release builds.

Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (2)

12-28: Well-structured error container for array decoding results [HIGH]

The ArrayDecodingError struct effectively encapsulates array decoding results using the Result type, with an efficient computed property for error extraction.


30-41: Verify the assertion for array decoding error reporting [MEDIUM]

The assertion assert(!wasReported) on line 36 enforces that array decoding errors should not have been reported. Please confirm this is the intended behavior - should array decoding errors never be reported through the error reporter?

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2)

12-25: Clean implementation of decode with outcome tracking [HIGH]

The method properly handles optional decoding with appropriate outcome tracking for debug builds while maintaining clean behavior for release builds.


27-67: Comprehensive decoding implementation with proper error handling [HIGH]

The method excellently handles all decoding scenarios:

  • Explicit key existence and null value checks
  • Proper error reporting through the decoder
  • Clean array element decoding using PolymorphicValue
  • Appropriate outcome tracking for each scenario

The pattern of reporting errors before rethrowing is correct for maintaining error context.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1)

12-172: Excellent test coverage for LossyArray resilient decoding [HIGH]

The test suite provides comprehensive coverage including:

  • Partial decoding with invalid elements
  • Detailed error tracking through projected values
  • Integration with JSONDecoder error reporting
  • Complete failure scenarios

The Result extension is a nice utility addition for testing.

Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (1)

12-124: Well-structured tests for DateValue resilient decoding [HIGH]

The test suite effectively covers:

  • Multiple date decoding strategies (ISO8601, RFC3339, Timestamp)
  • Success and failure scenarios
  • Null value handling
  • Error reporting integration

Good use of conditional compilation for debug-only features.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (2)

16-28: Excellent resilient decoding implementation! [HIGH]

The explicit key existence and null value checks provide clear outcome tracking while maintaining proper decoding behavior. This pattern is consistent with other polymorphic extensions in the codebase.


34-47: Proper optional decoding semantics with outcome tracking. [HIGH]

The method correctly returns nil for missing keys while still providing detailed outcome information for null values. The use of superDecoder maintains proper delegation to the wrapper's decoding logic.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (4)

29-40: Well-designed outcome tracking integration. [HIGH]

The new outcome property and initializers properly integrate resilient decoding while maintaining backward compatibility. The internal initializer enables precise outcome specification from decoding extensions.


42-52: Excellent debug-only projected value design. [HIGH]

The conditional compilation ensures projected values are only available for debugging while preventing production misuse through the Never return type and fatalError. This balances utility with safety effectively.


66-73: Correct error-throwing semantics with outcome tracking. [HIGH]

The implementation properly maintains the non-lossy behavior by rethrowing errors while still tracking the outcome. This correctly differentiates from lossy variants that recover from errors.


76-86: Proper Equatable and Hashable implementations. [MEDIUM]

The explicit implementations correctly base equality and hashing on the wrappedValue, treating the outcome as internal state. This follows expected value semantics for property wrappers.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (3)

11-28: Clean protocol design with robust error extraction. [HIGH]

The protocol provides a consistent interface for polymorphic projected values, and the default error extraction properly handles all ResilientDecodingOutcome cases. The pattern matching logic is comprehensive and correct.


30-41: Appropriate basic projected value implementation. [MEDIUM]

The simple struct design is well-suited for polymorphic wrappers that only need basic outcome tracking without additional metadata.


43-58: Excellent specialized projected value for lossy arrays. [HIGH]

The addition of per-element Result<T, Error> tracking is perfect for lossy array operations where individual element failures need to be inspected. This provides the right level of detail for debugging complex polymorphic array decoding.

Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (3)

29-39: Consistent basic projected value design. [MEDIUM]

The implementation follows the established pattern while being appropriately tailored for BetterCodable property wrappers. The consistency across wrapper categories is beneficial for developer experience.


41-58: Brilliant use of dynamic member lookup for error access. [HIGH]

The @dynamicMemberLookup integration with arrayDecodingError() provides seamless access to detailed array decoding diagnostics. This creates a powerful debugging experience while maintaining a clean API surface.


60-78: Excellent dictionary error access with consistent design. [HIGH]

The dictionary version maintains the same powerful @dynamicMemberLookup pattern while properly adapting to dictionary semantics with the Key: Hashable constraint. The consistency across collection types is excellent.

Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (4)

32-43: Consistent outcome tracking integration. [HIGH]

The implementation follows the same excellent pattern established in other polymorphic wrappers, providing consistent API design and enabling precise outcome specification from decoding extensions.


45-55: Consistent debug-only projected value design. [HIGH]

Maintains the excellent pattern of DEBUG-only projected values with production safety through fatalError. The consistency across all polymorphic wrappers provides predictable developer experience.


69-79: Excellent lossy decoding with proper error reporting. [HIGH]

The implementation correctly maintains lossy semantics while upgrading from print to decoder.reportError(error) for proper integration with the resilient decoding infrastructure. The wasReported: true parameter accurately reflects the error handling.


82-92: Proper equality semantics consistent with framework patterns. [MEDIUM]

The implementations correctly base equality and hashing on wrappedValue only, treating outcome as internal state. This maintains consistency with other resilient decoding wrappers throughout the framework.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (3)

40-62: Well-designed resilient decoding infrastructure [HIGH]

The addition of ResilientDecodingOutcome tracking and debug-only projected values provides excellent debugging capabilities without runtime overhead in release builds. The dual initializer pattern cleanly separates internal outcome tracking from public API usage.


121-131: Explicit protocol conformances improve type safety [MEDIUM]

The explicit implementations of Equatable and Hashable conformances are well-designed and properly constrained to only when the expected type supports these protocols. This replaces previous empty extensions and provides clear semantics.


66-102: Solid decoding logic with proper error propagation [HIGH]

The explicit nil checking using singleValueContainer() before attempting array decoding is correct and handles the edge case where JSON contains explicit null. The error reporting via decoder.reportError(error) maintains consistency with the framework's centralized error tracking.

However, consider a potential optimization concern:

The current approach creates both a single-value container and an unkeyed container. Consider caching the container check:

public init(from decoder: Decoder) throws {
-  // First check if the value is nil
-  let container = try decoder.singleValueContainer()
-  
-  if container.decodeNil() {
+  // Check for nil first, then decode array
+  if try decoder.singleValueContainer().decodeNil() {
    // Value is explicitly nil
    #if DEBUG
    self.init(wrappedValue: nil, outcome: .valueWasNil)
    #else
    self.init(wrappedValue: nil)
    #endif
    return
  }

Likely an incorrect or invalid review comment.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (4)

15-28: Excellent test fixture design for comprehensive scenarios [HIGH]

The test fixture with multiple @DummyNotice.OptionalPolymorphicArray properties enables testing various decoding states simultaneously. This pattern effectively validates the independent behavior of each property wrapper instance.


34-74: Thorough validation of successful decoding paths [HIGH]

These tests properly verify both the decoded values and the polymorphic type resolution. The validation of specific polymorphic types (DummyCallout, DummyActionableCallout) and their properties ensures the polymorphic strategy works correctly in real-world scenarios.


138-188: Robust error reporting validation [HIGH]

The error reporting tests properly validate integration with the centralized error reporting mechanism. Using decoder.enableResilientDecodingErrorReporting() and flushReportedErrors() ensures errors are captured and can be inspected for debugging purposes.


192-247: Comprehensive projected value testing enhances debugging capabilities [MEDIUM]

The DEBUG-only projected value tests validate that decoding outcomes are properly tracked and exposed. This provides excellent debugging capabilities while maintaining zero runtime cost in release builds.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (2)

16-46: Excellent resilient decoding with proper fallback behavior [HIGH]

The enhanced decode method properly handles all edge cases:

  • Missing keys return empty array with .keyNotFound outcome
  • Null values return empty array with .valueWasNil outcome
  • Decoding failures return empty array with recovery outcome

The use of superDecoder(forKey:) for delegation is the correct pattern and maintains proper decoding context.


48-69: Consistent optional decoding implementation [HIGH]

The decodeIfPresent method correctly follows the optional pattern by returning nil for missing keys, which differentiates it from the required decode method. The explicit key existence check and null handling maintain consistency with the required variant.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (4)

19-32: Clever test design for non-convertible types [MEDIUM]

The NestedObject type that implements LosslessStringConvertible but always returns nil from init?(_ description: String) is a smart way to test conversion failures. This ensures the lossy behavior is properly tested when objects cannot be converted to strings.


34-69: Comprehensive validation of projected value error tracking [HIGH]

The test excellently validates that the projected value exposes detailed information about each element's conversion attempt, including both successful conversions and failures. The assertions on results.count and errors.count provide precise validation of the lossy conversion behavior.


71-101: Solid integration testing with error reporting system [HIGH]

The error reporting test properly validates that conversion failures are reported to the centralized error reporting system. The use of enableResilientDecodingErrorReporting() and verification of reported errors ensures the integration works correctly.


103-138: Appropriate failure behavior validation [MEDIUM]

The tests correctly validate that completely invalid input (non-arrays) and missing required keys result in decoding failures rather than empty arrays. This maintains the distinction between lossy element conversion and structural decoding requirements.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (4)

11-30: Clean test structure with appropriate outcome validation [HIGH]

The empty array test correctly validates that an empty array decodes successfully with .decodedSuccessfully outcome rather than any fallback state. This properly distinguishes between "empty by design" and "empty due to fallback."


69-103: Excellent validation of fallback behavior on invalid elements [HIGH]

This test properly validates the key behavior difference: when any element in the array is invalid, the entire array falls back to empty with a recovery outcome. This is correct behavior for DefaultEmptyPolymorphicArrayValue and differs appropriately from lossy variants that would skip invalid elements.


105-143: Comprehensive edge case coverage [HIGH]

The missing key and null value tests properly validate the fallback behavior while tracking appropriate outcomes (.keyNotFound, .valueWasNil). This ensures the wrapper provides predictable behavior in all edge cases while maintaining debugging capabilities.


170-206: Robust error reporting validation [HIGH]

The error reporting test properly validates that decoding failures are captured by the centralized error reporting system, enabling production debugging while maintaining the fallback behavior that keeps the application functional.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1)

33-73: Comprehensive test coverage for successful decoding [HIGH]

Excellent test coverage! The test properly validates both the decoded values and the resilient decoding outcomes in DEBUG mode. The verification of all elements succeeding is thorough.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (2)

92-121: Robust lossy array decoding implementation [HIGH]

The decoding implementation correctly handles failures by:

  1. Continuing to process remaining elements after failures
  2. Using AnyDecodableValue to advance the container position
  3. Tracking both successful values and failure details in DEBUG mode

This is a well-thought-out approach for lossy decoding.


133-143: Explicit protocol conformances improve clarity [HIGH]

Good practice implementing explicit Equatable and Hashable conformances rather than relying on automatic synthesis. This makes the behavior clear and predictable.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (1)

108-111: Good defensive programming for integer keys [HIGH]

Excellent change from throwing an error to silently skipping non-integer keys. This aligns well with the lossy behavior expected from this wrapper.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (2)

25-84: Excellent comprehensive test for projected value error information [HIGH]

This test thoroughly validates:

  1. Correct filtering of invalid entries
  2. Detailed error tracking in projected values
  3. Different dictionary key types (String and Int)
  4. Nested object decoding

The test provides great coverage of the resilient decoding features.


140-144: Clear distinction between null and error cases [HIGH]

Good test design showing that null values are handled differently from actual decoding errors. This distinction is important for understanding the wrapper's behavior.

Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (3)

19-29: Well-designed isFrozen property for backward compatibility [HIGH]

The isFrozen property with a default of false is an excellent design choice. It allows existing code to continue working while providing an opt-in mechanism for stricter error handling with unknown enum values.


210-278: Comprehensive RawRepresentable handling with clear error semantics [HIGH]

Excellent implementation that:

  1. Distinguishes between frozen and non-frozen strategies
  2. Uses appropriate error types (DecodingError vs UnknownNovelValueError)
  3. Maintains consistent error reporting
  4. Provides clear debug information

This will greatly help with debugging enum decoding issues in production.


157-208: Robust Bool decoding with multiple fallback strategies [HIGH]

The implementation correctly handles Bool values that might be encoded as integers or strings, which is common in real-world APIs. The error reporting for type mismatches is properly integrated.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (6)

25-36: Good defensive programming with assertion failure handling

The implementation correctly uses assertionFailure() to catch programming errors during development while gracefully handling the edge case in production by setting the mayBeMissingReportedErrors flag.


44-57: Well-structured error reporter lifecycle management

The method properly isolates error reporting per decode operation by creating a fresh reporter, saving/restoring the previous state, and flushing errors at the end. This ensures thread safety and prevents error accumulation across decode operations.


62-79: Efficient error reporter implementation [MEDIUM]

The class efficiently tracks errors with a simple boolean flag and only creates ErrorDigest objects when necessary. The state management in flushReportedErrors() properly resets for reuse.


81-117: Well-designed hierarchical error storage

The ErrorDigest and nested Node structure effectively organize errors by coding path, with proper support for filtering specific error types like UnknownNovelValueError. The recursive implementation is clean and efficient.


121-134: Robust error reporting integration with proper error handling

The method correctly handles the opt-in nature of error reporting and includes appropriate debug assertions for type safety violations. The coding path conversion is properly implemented.


138-183: Excellent debug-only error formatting implementation

The debug printing provides clear, hierarchical error output without impacting release builds. The specialized handling of DecodingError cases and the note about UnknownNovelValueError not being reported by default are particularly helpful for developers.

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (4)

220-242: Well-structured test enums for frozen/non-frozen behavior [HIGH]

Excellent test setup demonstrating the behavioral difference between frozen and non-frozen enums. This clearly shows how the isFrozen property affects error reporting for unknown raw values.


244-269: Comprehensive validation of successful enum decoding

The test properly verifies both the decoded values and the success outcomes in debug mode.


271-342: Excellent coverage of frozen vs non-frozen enum error handling [HIGH]

These tests clearly demonstrate the critical behavioral difference:

  • Non-frozen enums recover gracefully with UnknownNovelValueError
  • Frozen enums fail with DecodingError and report to the error reporter

This distinction is important for API evolution and backward compatibility.


344-425: Thorough edge case coverage for RawRepresentable types

The remaining tests comprehensively cover missing keys, null values, and type mismatches, ensuring robust fallback behavior in all scenarios.

Copy link
Contributor

@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: 5

🔭 Outside diff range comments (2)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

50-63: Add error reporting for failed array element decoding [MEDIUM]

Unlike other array wrappers in the codebase, PolymorphicArrayValue doesn't report errors when individual elements fail to decode. For consistency with the resilient decoding pattern, consider catching and reporting element errors.

 public init(from decoder: Decoder) throws {
   var container = try decoder.unkeyedContainer()
 
   var elements = [PolymorphicType.ExpectedType]()
   while !container.isAtEnd {
-    let value = try container.decode(PolymorphicValue<PolymorphicType>.self).wrappedValue
-    elements.append(value)
+    do {
+      let value = try container.decode(PolymorphicValue<PolymorphicType>.self).wrappedValue
+      elements.append(value)
+    } catch {
+      decoder.reportError(error)
+      throw error
+    }
   }
 
   self.wrappedValue = elements
   self.outcome = .decodedSuccessfully
 }
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (1)

191-197: Consider documenting the Bool fallback behavior [LOW]

The fallback logic for Bool values (trying Int then String conversions) is a nice feature but could benefit from inline documentation explaining when each fallback is used.

Add a comment explaining the conversion logic:

       }
+      // Fallback: Try to decode as Int (e.g., 0/1) or String (e.g., "true"/"false")
       if let intValue = try? decodeIfPresent(Int.self, forKey: key),
          let bool = Bool(exactly: NSNumber(value: intValue)) {
         return DefaultCodable(wrappedValue: bool)
       } else if let stringValue = try? decodeIfPresent(String.self, forKey: key),
                 let bool = Bool(stringValue) {
         return DefaultCodable(wrappedValue: bool)
♻️ Duplicate comments (8)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)

37-37: Remove unused recoveredFromDebugOnlyError property

This static property is never used in the codebase. The recoveredFrom method always creates a new instance.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (1)

52-68: Consider tracking fallback decoding for better diagnostics [MEDIUM]

Both direct decoding (line 56) and fallback decoding (line 67) set the outcome to .decodedSuccessfully. For better debugging insights, consider differentiating these paths.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

43-46: Improve fatal error message for production safety [MEDIUM]

The error message should clearly indicate this is a development-time error and guide developers on proper usage.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)

82-85: Fix inconsistent error reporting flag

The individual element errors are already reported via elementDecoder.reportError(error) on line 72, but the aggregate error is marked as wasReported: false. This should be true for consistency.

       } else {
         let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
-        self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
+        self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: true))
       }
Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (1)

52-57: Remove fatalError in production builds [HIGH]

The fatalError for projected value access in non-DEBUG builds could crash production apps. Consider removing the property entirely in non-DEBUG builds for compile-time safety.

   #if DEBUG
   /// The projected value providing access to decoding outcome
   public var projectedValue: PolymorphicProjectedValue {
     return PolymorphicProjectedValue(outcome: outcome)
   }
-  #else
-  /// In non-DEBUG builds, accessing projectedValue is a programmer error
-  public var projectedValue: Never {
-    fatalError("@\(Self.self) projectedValue should not be used in non-DEBUG builds")
-  }
   #endif
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (1)

46-60: Simplify redundant nested error handling

The nested try-catch blocks perform identical error handling. Merge them for cleaner code.

   public init(from decoder: Decoder) throws {
     do {
       let value = try Formatter.RawValue(from: decoder)
-      do {
-        self.wrappedValue = try Formatter.decode(value)
-        self.outcome = .decodedSuccessfully
-      } catch {
-        decoder.reportError(error)
-        throw error
-      }
+      self.wrappedValue = try Formatter.decode(value)
+      self.outcome = .decodedSuccessfully
     } catch {
       decoder.reportError(error)
       throw error
     }
   }
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (1)

76-86: Memory concern already noted [MEDIUM]

The projectedValue exposes detailed per-element results which could consume significant memory for large arrays in DEBUG builds.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (1)

84-127: Replace force casts with safe alternatives [HIGH]

The force casts in this section could cause runtime crashes. Since you've already verified the key types through conditional checks, use safer patterns instead of force casting.

Apply this diff to fix the force casts:

-          elements[stringKey as! Key] = value
+          if let key = stringKey as? Key {
+            elements[key] = value
+          }
-          results[stringKey as! Key] = .success(value)
+          if let key = stringKey as? Key {
+            results[key] = .success(value)
+          }
-          results[stringKey as! Key] = .failure(error)
+          if let key = stringKey as? Key {
+            results[key] = .failure(error)
+          }
-          elements[key.intValue! as! Key] = value
+          if let intValue = key.intValue, let key = intValue as? Key {
+            elements[key] = value
+          }
-          results[key.intValue! as! Key] = .success(value)
+          if let intValue = key.intValue, let key = intValue as? Key {
+            results[key] = .success(value)
+          }
-          results[key.intValue! as! Key] = .failure(error)
+          if let intValue = key.intValue, let key = intValue as? Key {
+            results[key] = .failure(error)
+          }
📜 Review details

Configuration used: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98b830a and ef0fb0f.

📒 Files selected for processing (17)
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (6 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (3 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (4 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (3 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (4 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
Sources/KarrotCodableKit/**

⚙️ CodeRabbit Configuration File

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift
🧬 Code Graph Analysis (5)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (4)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)
  • lhs (70-72)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (1)
  • lhs (86-88)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • lhs (19-30)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (6)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (2)
  • lhs (70-72)
  • hash (76-78)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (2)
  • lhs (86-88)
  • hash (92-94)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2)
  • lhs (122-124)
  • hash (128-130)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (1)
  • lhs (180-182)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • lhs (19-30)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2)
  • lhs (64-66)
  • hash (70-72)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (3)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (5)
  • decode (120-149)
  • decode (157-208)
  • decode (215-278)
  • lhs (99-101)
  • hash (105-107)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • lhs (19-30)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (5)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2)
  • lhs (122-124)
  • hash (128-130)
Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2)
  • lhs (83-85)
  • hash (89-91)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • lhs (19-30)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2)
  • lhs (64-66)
  • hash (70-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Check Macro Compatibility
  • GitHub Check: Build and Test (16.1, release)
🔇 Additional comments (23)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (1)

67-72: Fix incorrect outcome determination logic [HIGH]

The condition elements.count == results.count will always be true when all elements decode successfully, leading to the wrong outcome assignment. When some elements fail to decode, elements.count < results.count, so the else branch executes correctly. However, the logic is inverted.

-    if elements.count == results.count {
-      self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
-    } else {
-      let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
-      self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
-    }
+    let hasFailures = results.contains(where: { if case .failure = $0 { return true } else { return false } })
+    if hasFailures {
+      let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
+      self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
+    } else {
+      self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
+    }

Likely an incorrect or invalid review comment.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)

11-28: Well-designed protocol with comprehensive error extraction [HIGH]

The protocol design is clean and the default error extraction correctly handles all ResilientDecodingOutcome cases. The pattern matching covers success states (returning nil) and error recovery states (returning the captured error).

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (4)

29-40: Correct resilient decoding outcome tracking [HIGH]

The implementation properly tracks decoding outcomes with a new outcome property and provides both a public initializer (defaulting to success) and an internal initializer for explicit outcome setting.


42-52: Consistent projected value pattern [HIGH]

The conditional projectedValue implementation follows the established pattern - providing debug diagnostics in DEBUG builds while preventing misuse in production with a clear fatal error message.


66-74: Correct error handling for non-lossy wrapper [HIGH]

The decoding implementation correctly follows the documented behavior - setting .decodedSuccessfully on success and rethrowing errors instead of recovering. This aligns with the wrapper's purpose as stated in the documentation.


76-86: Appropriate Equatable/Hashable implementation [HIGH]

The explicit conformances correctly compare/hash only the wrappedValue, ignoring the outcome property. This is semantically correct since the outcome is metadata about the decoding process, not part of the logical value.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (4)

40-54: Well-implemented outcome tracking [HIGH]

The outcome tracking follows the established pattern with appropriate initializers. The internal initializer allows explicit outcome setting while the public one defaults to success.


66-77: Correct nil value handling [HIGH]

The implementation properly checks for explicit nil values first using singleValueContainer().decodeNil(), setting the appropriate .valueWasNil outcome. This correctly distinguishes between missing keys and explicit null values.


80-102: Proper error reporting and rethrow pattern [HIGH]

The decoding logic correctly:

  • Attempts to decode as an unkeyed container (array)
  • Uses PolymorphicValue<PolymorphicType> for individual elements
  • Reports errors via decoder.reportError(error) before rethrowing
  • Sets appropriate outcomes for success/failure

This follows the documented behavior of throwing errors rather than recovering.


121-131: Consistent equality semantics [HIGH]

The Equatable and Hashable implementations correctly focus on the wrappedValue only, maintaining semantic equality regardless of decoding outcome metadata.

Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (4)

32-43: Proper outcome tracking for lossy wrapper [HIGH]

The outcome tracking implementation correctly includes both initializers - public one defaulting to success and internal one for explicit outcome setting. This supports the resilient decoding infrastructure.


69-79: Improved error reporting with proper recovery tracking [HIGH]

Excellent improvement! The change from print(error) to decoder.reportError(error) integrates with the centralized error reporting system. The outcome correctly reflects recovery with wasReported: true, indicating the error was properly reported.


45-55: Consistent projected value pattern [HIGH]

The conditional projectedValue implementation maintains consistency across the framework - debug diagnostics in DEBUG builds with clear production safeguards.


82-92: Semantically correct equality implementation [HIGH]

The Equatable and Hashable conformances appropriately compare only the wrappedValue, treating the decoding outcome as metadata rather than part of the logical value.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (2)

43-74: Complex but necessary dual-mode initialization [HIGH]

The dual initialization pattern (DEBUG vs non-DEBUG) properly handles the different data structures needed. The DEBUG version tracks individual results while non-DEBUG keeps only the essential outcome information.


133-143: Appropriate equality semantics for array wrapper [HIGH]

The Equatable and Hashable implementations correctly focus on the wrappedValue array content, treating decoding metadata as non-semantic information.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (7)

27-27: LGTM! Good addition for resilient decoding infrastructure.

The outcome property effectively tracks decoding results for debugging and monitoring. Making it public let ensures immutability while providing access to decoding diagnostics.

Performance consideration: This adds a small memory overhead to each PolymorphicValue instance, but the debugging benefits likely outweigh the cost for a runtime library focused on robust decoding.


30-38: Excellent API design for resilient decoding support.

The dual initializer approach is well-designed:

  • Public initializer maintains backward compatibility with sensible default (.decodedSuccessfully)
  • Internal initializer provides framework flexibility for outcome injection
  • Clear separation between user-facing and internal APIs

This follows Swift API guidelines and maintains clean encapsulation boundaries.


52-61: Excellent resilient decoding implementation! [HIGH]

The enhanced Decodable implementation strikes the perfect balance:

Backward Compatibility: Maintains existing throwing behavior
Error Tracking: Centralized error reporting via decoder.reportError
Outcome Recording: Proper success/failure state tracking
Clean Recovery: Error reporting before rethrowing preserves original error context

This approach enables production debugging without breaking existing codebases - exactly what's needed for a runtime library.


63-67: Correct semantic equality implementation.

The Equatable conformance properly focuses on the wrappedValue rather than implementation details like outcome. This ensures that two PolymorphicValue instances with identical semantic content are considered equal, regardless of their decoding history.

This follows Swift's property wrapper equality conventions and maintains logical consistency.


69-73: Consistent and correct Hashable implementation.

The Hashable conformance correctly aligns with the Equatable implementation by hashing only the wrappedValue. This maintains the fundamental hash/equality contract while using proper hasher.combine for hash computation.

Well-designed consistency across protocol conformances.


23-76: Outstanding resilient decoding enhancement! [HIGH]

This implementation demonstrates excellent software engineering practices:

🎯 Comprehensive Enhancement: Seamlessly integrates outcome tracking, error reporting, and debug introspection
🔒 API Stability: Maintains full backward compatibility while adding new capabilities
Performance Conscious: Debug-only projected values and minimal memory overhead
🛠️ Developer Experience: Centralized error reporting enables production debugging

The changes follow a consistent pattern across the KarrotCodableKit and provide exactly what's needed for robust polymorphic decoding in production environments. The balance between functionality and performance is spot-on for a runtime library.


40-42: PolymorphicProjectedValue definition confirmed

I verified that PolymorphicProjectedValue is defined in
• Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (around line 34)

The debug-only projectedValue implementation is safe, avoids production overhead, and follows Swift property-wrapper conventions. LGTM!

@ElonPark ElonPark force-pushed the feature/elon/add-resilient-decoding branch from ef0fb0f to 3eb4df4 Compare July 29, 2025 12:58
ElonPark added 9 commits July 29, 2025 22:10
- Add ErrorReporting.swift for error tracking and reporting
- Add ResilientDecodingOutcome enum for decoding results
- Add ArrayDecodingError for array-specific error handling
- Add DictionaryDecodingError for dictionary-specific error handling
- Add ResilientProjectedValue base implementation
- Add ResilientArrayProjectedValue for array property wrappers
- Add ResilientDictionaryProjectedValue for dictionary property wrappers
- Add PolymorphicProjectedValue base implementation
- Add PolymorphicLossyArrayProjectedValue for lossy array operations
- Move PolymorphicEnumCodableTests.swift to Enum/ directory
- Move PolymorphicEnumDecodableTests.swift to Enum/ directory
- Move PolymorphicValueTests.swift to Value/ directory
…bleTests

Improve test safety by using #require instead of force unwrapping
- Add outcome tracking to DefaultCodable property wrapper
- Implement projected value for error reporting
- Handle nil values and decoding errors gracefully
- Add comprehensive tests for resilient behavior
- Add outcome tracking to DataValue property wrapper
- Implement projected value for error reporting
- Handle decoding errors with proper error tracking
- Add comprehensive tests for resilient behavior
- Add outcome tracking to DateValue and OptionalDateValue
- Implement projected value for error reporting
- Handle decoding errors with proper error tracking
- Add comprehensive tests for resilient behavior
Copy link
Contributor

@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: 6

♻️ Duplicate comments (29)
README.md (1)

280-280: Address formatting consistency as previously noted [LOW]

The acknowledgement addition is appropriate for the resilient decoding features. However, as noted in the previous review, this line needs a trailing period for consistency with other list items and a final newline to satisfy markdown-lint requirements.

Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (1)

1-123: Excellent test coverage with all comments now in English!

I see that the Korean comments mentioned in the past review have been successfully translated to English. The test suite comprehensively covers:

  • Successful decoding with projected value verification
  • Invalid Base64 format handling
  • Null value handling
  • Error reporting integration with JSONDecoder

The conditional compilation for debug-only projected value testing is well implemented.

Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (2)

37-38: Unused static property should be removed or utilized.

The recoveredFromDebugOnlyError property is still unused. Either remove it or modify the recoveredFrom method to use it when wasReported is true.

Option 1 - Remove unused property:

  public static let decodedSuccessfully = Self()
  public static let keyNotFound = Self()
  public static let valueWasNil = Self()
- public static let recoveredFromDebugOnlyError = Self()
  public static func recoveredFrom(_: any Error, wasReported: Bool) -> Self { Self() }

Option 2 - Use the property:

  public static let recoveredFromDebugOnlyError = Self()
- public static func recoveredFrom(_: any Error, wasReported: Bool) -> Self { Self() }
+ public static func recoveredFrom(_: any Error, wasReported: Bool) -> Self {
+   wasReported ? recoveredFromDebugOnlyError : Self()
+ }

18-31: Add documentation for the Equatable implementation's error comparison behavior.

The implementation ignores actual error values when comparing .recoveredFrom cases, which should be documented to prevent confusion.

+/// Two `ResilientDecodingOutcome` values are considered equal if they represent the same
+/// outcome case. For `.recoveredFrom` cases, only the `wasReported` flag is compared;
+/// the actual error values are ignored for equality purposes.
 extension ResilientDecodingOutcome: Equatable {
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (1)

58-75: Consider implementing distinct outcomes for direct vs fallback decoding.

Both direct decoding (line 62) and fallback decoding (line 74) set outcome = .decodedSuccessfully. For better debugging visibility, consider using .recoveredFrom(error, wasReported: false) for the fallback case, which would indicate that type conversion was necessary while still preserving the successful result.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1)

47-75: Consider testing for distinct fallback decoding outcome.

The test currently expects .decodedSuccessfully even when fallback decoding occurs. If the implementation is updated to use a different outcome like .recoveredFrom() for fallback cases, this test should be updated accordingly to verify the more granular outcome.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)

38-47: Optimize superDecoder usage and improve error handling.

Creating superDecoder twice is inefficient, and the error reporting could fail silently if the second decoder creation fails.

Apply this diff:

 // Try to decode the polymorphic value
+let decoder = try superDecoder(forKey: key)
 do {
-  let decoder = try superDecoder(forKey: key)
   let value = try T.decode(from: decoder)
   return LossyOptionalPolymorphicValue(wrappedValue: value, outcome: .decodedSuccessfully)
 } catch {
   // Report error to resilient decoding error reporter
-  let decoder = try? superDecoder(forKey: key)
-  decoder?.reportError(error)
+  decoder.reportError(error)
   return LossyOptionalPolymorphicValue(wrappedValue: nil, outcome: .recoveredFrom(error, wasReported: true))
 }
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (1)

47-55: Simplify nested error handling to avoid redundancy.

The nested do-catch blocks both perform identical error reporting and rethrowing, creating unnecessary complexity.

 public init(from decoder: Decoder) throws {
   do {
     let value = try Formatter.RawValue(from: decoder)
-    do {
-      self.wrappedValue = try Formatter.decode(value)
-      self.outcome = .decodedSuccessfully
-    } catch {
-      decoder.reportError(error)
-      throw error
-    }
+    self.wrappedValue = try Formatter.decode(value)
+    self.outcome = .decodedSuccessfully
   } catch {
     decoder.reportError(error)
     throw error
   }
 }
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)

47-60: Simplify redundant nested error handling.

Both catch blocks perform identical error reporting and rethrowing, creating unnecessary code duplication.

 public init(from decoder: Decoder) throws {
   do {
     let stringValue = try String(from: decoder)
-    do {
-      self.wrappedValue = try Coder.decode(stringValue)
-      self.outcome = .decodedSuccessfully
-    } catch {
-      decoder.reportError(error)
-      throw error
-    }
+    self.wrappedValue = try Coder.decode(stringValue)
+    self.outcome = .decodedSuccessfully
   } catch {
     decoder.reportError(error)
     throw error
   }
 }
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

37-47: Enhance fatalError message for better debugging.

The error message could provide more context about why accessing projectedValue in production is a programmer error.

 public var projectedValue: Never {
-  fatalError("@\(Self.self) projectedValue should not be used in non-DEBUG builds")
+  fatalError("@\(Self.self) projectedValue is only available in DEBUG builds for decoding diagnostics. Remove usage before shipping to production.")
 }
Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (1)

47-57: Remove fatalError to prevent production crashes.

The current fatalError implementation could crash production apps if projectedValue is accidentally accessed. A compile-time approach is safer.

-#else
-/// In non-DEBUG builds, accessing projectedValue is a programmer error
-public var projectedValue: Never {
-  fatalError("@\(Self.self) projectedValue should not be used in non-DEBUG builds")
-}
-#endif
+#endif

This removes the fatalError completely in non-DEBUG builds, making it a compile-time error instead of a runtime crash.

Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)

47-64: Simplify nested error handling structure [MEDIUM]

The inner try-catch block only reports and rethrows the error, which duplicates the outer catch behavior.

   public init(from decoder: Decoder) throws {
     do {
       let value = try Formatter.RawValue(from: decoder)
-      do {
-        self.wrappedValue = try Formatter.decode(value)
-        self.outcome = .decodedSuccessfully
-      } catch {
-        decoder.reportError(error)
-        throw error
-      }
+      self.wrappedValue = try Formatter.decode(value)
+      self.outcome = .decodedSuccessfully
     } catch DecodingError.valueNotFound(let rawType, _) where rawType == Formatter.RawValue.self {
       self.wrappedValue = nil
       self.outcome = .valueWasNil
     } catch {
       decoder.reportError(error)
       throw error
     }
   }
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1)

6-6: Consider shortening the type name to comply with SwiftLint rules [LOW]

The type name LossyOptionalPolymorphicValueResilientTests is 46 characters, exceeding the 40-character limit. Consider shortening it while maintaining clarity.

-struct LossyOptionalPolymorphicValueResilientTests {
+struct LossyOptionalPolymorphicResilientTests {
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)

47-58: Consider memory implications for large arrays [MEDIUM]

The PolymorphicLossyArrayProjectedValue stores a results array with individual Result<T, Error> entries for each element. For large arrays, this could consume significant memory in DEBUG builds.

Consider adding a configurable limit on tracked results:

public struct PolymorphicLossyArrayProjectedValue<T>: PolymorphicProjectedValueProtocol {
  /// The outcome of the decoding process
  public let outcome: ResilientDecodingOutcome
  
  /// Results of decoding each element in the array (limited for memory efficiency)
  public let results: [Result<T, Error>]
  
  /// Maximum number of individual results to track
  private static let maxTrackedResults = 1000
  
  public init(outcome: ResilientDecodingOutcome, results: [Result<T, Error>]) {
    self.outcome = outcome
    // Limit results to prevent memory issues with very large arrays
    self.results = Array(results.prefix(Self.maxTrackedResults))
  }
}
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)

71-85: Inconsistent error reporting flag [MEDIUM]

Individual element errors are reported via elementDecoder.reportError(error), but the aggregate error is marked as wasReported: false.

       } else {
         let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
-        self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
+        self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: true))
       }
Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1)

11-27: Protocol duplication is acceptable for module separation [MEDIUM]

While there's duplication with PolymorphicProjectedValueProtocol, maintaining separate protocols for BetterCodable and PolymorphicCodable modules provides clear architectural boundaries and allows for independent evolution of each module's resilient decoding features.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)

62-62: Minor inconsistency in DEBUG vs release initialization [LOW]

There's a subtle inconsistency in the decodeIfPresent null handling where the DEBUG path includes the outcome parameter but the release path doesn't.

    if try decodeNil(forKey: key) {
      #if DEBUG
      return PolymorphicLossyArrayValue(wrappedValue: [], outcome: .valueWasNil, results: [])
      #else
-     return PolymorphicLossyArrayValue(wrappedValue: [])
+     return PolymorphicLossyArrayValue(wrappedValue: [], outcome: .valueWasNil)
      #endif
    }
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1)

117-134: Improve test assertions for array element results [LOW]

The current pattern of using if case with Issue.record is verbose. Consider using helper assertions or the Testing framework's pattern matching capabilities.

-    if case .success = result.$notices.results[0] {} else {
-      Issue.record("Expected success at index 0")
-    }
+    #expect(result.$notices.results[0].isSuccess)

You could add an extension to make this cleaner:

extension Result {
  var isSuccess: Bool {
    if case .success = self { return true }
    return false
  }
}
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (2)

47-49: Consider memory implications of storing per-element results [MEDIUM]

In DEBUG mode, storing detailed results for every array element could have significant memory overhead for large arrays. Consider adding a limit or making this behavior configurable.

#if DEBUG
/// Maximum number of individual results to track (prevents memory issues with large arrays)
private static let maxTrackedResults = 1000
#endif

117-117: Consider outcome semantics for partial failures [MEDIUM]

The implementation always sets outcome = .decodedSuccessfully even when some array elements fail to decode. While the lossy behavior is correct, consider whether the outcome should reflect partial success when some elements were skipped.

Consider tracking partial success in the outcome:

let hasFailures = results.contains { if case .failure = $0 { return true }; return false }
self.outcome = hasFailures ? .recoveredFrom(/* composite error */, wasReported: true) : .decodedSuccessfully

However, this may require defining what constitutes a "composite error" for multiple element failures.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1)

53-59: Use English for code comments [LOW]

Please use English for consistency with the rest of the codebase.

-    // Verify default behavior - only valid key-value pairs included
+    // Verify basic behavior - only valid key-value pairs are included
Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (4)

38-38: Translate Korean comments to English for consistency [LOW]

Please translate the Korean comments to English to maintain consistency across the codebase:

  • Line 38: // 기본 동작 검증 - 디코딩 실패 시 기본값 사용// Verify default behavior - use default value on decoding failure
  • Line 46: // projected value로 에러 정보 접근// Access error information via projected value
  • Line 53: // 에러 타입 확인// Check error type
  • Line 73: // 기본값 확인// Check default values
  • Line 81: // 키가 없을 때는 에러가 없음 (기본 동작)// No error when key is missing (default behavior)

Also applies to: 46-46, 53-53, 73-73, 81-81


106-106: Additional Korean comments to translate [LOW]

Please also translate these Korean comments:

  • Line 106: // 정상 값 확인// Check normal values
  • Line 114: // 성공적으로 디코딩된 경우 에러 없음// No error when successfully decoded
  • Line 139: // null일 때 기본값 사용// Use default value for null
  • Line 147: // null은 에러로 간주되지 않음// null is not considered an error

Also applies to: 114-114, 139-139, 147-147


176-176: Translate Korean comment [LOW]

Please translate: // 최소 3개의 에러가 리포트되어야 함// At least 3 errors should be reported


204-204: Translate comments [LOW]

Translate Korean comments:

  • Line 204: // 디코딩 실패 시 nil// nil on decoding failure
  • Line 213: // 에러 정보 확인// Check error info

Also applies to: 213-213

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (4)

21-49: Well-structured test for successful lossless conversions! [HIGH]

The test properly validates both the conversion behavior and the projected value access in DEBUG mode. Good coverage of different data types.


51-74: Correct validation of null value handling! [HIGH]

The test properly expects LosslessValue to throw on null values, which aligns with its non-optional design.


76-99: Excellent edge case coverage for complex types! [HIGH]

Testing with objects and arrays that cannot be losslessly converted provides good validation of the error handling.


101-136: Comprehensive error reporting integration test! [HIGH]

The test properly validates the error reporting pipeline with conditional DEBUG verification. Good coverage of the error collection mechanism.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef0fb0f and 3eb4df4.

📒 Files selected for processing (45)
  • README.md (1 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (6 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (3 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (4 hunks)
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (3 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (4 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (3 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift (5 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Tests/KarrotCodableKitTests/**

⚙️ CodeRabbit Configuration File

Tests/KarrotCodableKitTests/**: You are a senior Swift/iOS engineer reviewing runtime functionality tests for KarrotCodableKit's Codable extensions.

1. Runtime Behavior Testing [HIGH]

  • Verify JSON encoding/decoding scenarios cover real-world use cases
  • Check polymorphic type resolution testing across different strategies
  • Assess property wrapper testing (@DATEvalue, @DefaultFalse, etc.)
  • Validate error handling and edge case coverage

2. Test Data Quality [HIGH]

  • Review test JSON structures for realistic complexity
  • Check that TestDoubles provide comprehensive mock scenarios
  • Verify test data covers various data types and edge cases
  • Assess polymorphic test data represents actual usage patterns

3. Integration Testing [MEDIUM]

  • Check integration between macro-generated code and runtime functionality
  • Verify end-to-end scenarios combining multiple KarrotCodableKit features
  • Assess performance testing for large data structures
  • Review memory usage testing for complex polymorphic scenarios

Review Focus

  • Prioritize real-world usage scenario coverage
  • Focus on Codable correctness and performance implications
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
*

⚙️ CodeRabbit Configuration File

*: You are a senior Swift/iOS engineer with 15+ years of experience reviewing KarrotCodableKit code changes.

Review Priorities [HIGH]

  • Code quality: memory leaks, error handling, readability (100 chars/line)
  • Architecture: SOLID principles, design patterns, DRY/KISS
  • Swift best practices: modern features, memory management, performance

Communication

  • Mark priority: [HIGH], [MEDIUM], [LOW]
  • Provide specific code examples
  • Focus on high-impact improvements over style

Files:

  • README.md
**/*.md

⚙️ CodeRabbit Configuration File

**/*.md: You are a senior technical writer reviewing documentation for KarrotCodableKit, a Swift Codable extension library.

1. Technical Accuracy [HIGH]

  • Verify code examples compile and work correctly
  • Check that API documentation matches actual implementation
  • Validate macro usage examples demonstrate proper syntax
  • Assess JSON examples are well-formed and realistic

2. Documentation Completeness [HIGH]

  • Review coverage of all major features (CustomCodable, PolymorphicCodable, AnyCodable, BetterCodable)
  • Check that complex concepts like polymorphic type resolution are well explained
  • Verify installation and setup instructions are current
  • Assess troubleshooting and error handling guidance

3. User Experience [MEDIUM]

  • Evaluate documentation structure and navigation clarity
  • Check that examples progress from simple to complex appropriately
  • Verify code snippets include necessary imports and context
  • Assess whether documentation answers common user questions

Review Focus

  • Prioritize accuracy and completeness over style
  • Focus on developer experience and practical usage scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • README.md
Sources/KarrotCodableKit/**

⚙️ CodeRabbit Configuration File

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
🧬 Code Graph Analysis (7)
Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (3)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
  • arrayDecodingError (30-41)
Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)
  • dictionaryDecodingError (30-41)
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (5)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • decode (44-57)
  • reportError (129-140)
Sources/KarrotCodableKit/BetterCodable/DateValue/Strategy/ISO8601Strategy.swift (2)
  • decode (19-29)
  • decode (37-40)
Sources/KarrotCodableKit/BetterCodable/DateValue/Strategy/RFC3339NanoStrategy.swift (2)
  • decode (36-51)
  • decode (59-62)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)
  • lhs (70-72)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • lhs (19-30)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (16)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (129-140)
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (1)
  • lhs (66-68)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)
  • lhs (70-72)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (1)
  • lhs (86-88)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)
  • lhs (75-77)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (1)
  • lhs (99-101)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)
  • lhs (107-109)
Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (1)
  • lhs (92-94)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (1)
  • lhs (259-261)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (1)
  • lhs (77-79)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)
  • lhs (75-77)
Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (1)
  • lhs (83-85)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (1)
  • lhs (122-124)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (1)
  • lhs (134-136)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (1)
  • lhs (64-66)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • lhs (19-30)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (5)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
  • decodeIfPresent (31-48)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)
  • decodeIfPresent (23-48)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (1)
  • decodeIfPresent (27-67)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decodeIfPresent (23-46)
Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (5)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (5)
  • decode (120-149)
  • decode (157-208)
  • decode (215-272)
  • lhs (99-101)
  • hash (105-107)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • decode (44-57)
  • reportError (129-140)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (2)
  • recoveredFrom (38-38)
  • lhs (19-30)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2)
  • lhs (77-79)
  • hash (83-85)
🪛 SwiftLint (0.57.0)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift

[Warning] 6-6: Type name 'DefaultEmptyPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift

[Warning] 6-6: Type name 'LossyOptionalPolymorphicValueResilientTests' should be between 3 and 40 characters long

(type_name)

🔇 Additional comments (64)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1)

164-164: LGTM! Grammar correction improves test readability [LOW]

The correction from "thens" to "then" aligns with standard BDD-style test comment format and improves consistency across the test suite.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1)

120-120: LGTM! Consistent grammar improvement [LOW]

Good catch on the grammatical correction - maintains consistency with BDD test comment conventions throughout the codebase.

Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift (1)

59-59: Excellent safety improvement! [HIGH]

Replacing forced unwraps with try #require(json.data(using: .utf8)) is a great defensive programming practice. This prevents potential runtime crashes and provides clearer error reporting if the string-to-data conversion fails, while maintaining the existing test logic.

The use of Swift Testing's #require aligns perfectly with the framework's safety-first approach.

Also applies to: 85-85, 109-109, 128-128, 148-148

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (1)

116-116: Good consistency improvement in test comments [LOW]

Removing trailing colons from BDD-style comments creates a cleaner, more consistent format across the test suite. This aligns well with the coding guidelines' emphasis on consistency and readability.

Also applies to: 133-133, 136-136, 163-163

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)

12-21: LGTM! Clean implementation of resilient decoding for optional polymorphic values.

The method correctly handles the case where decodeIfPresent returns nil by creating an OptionalPolymorphicValue with .keyNotFound outcome, which accurately reflects that the key was not present in the container.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (2)

85-95: Good addition of Equatable and Hashable conformance.

The implementations correctly delegate to the wrapped array, maintaining expected semantics for equality and hashing based solely on the decoded values, not the decoding outcome.


66-72: Logic error in determining decoding outcome.

The condition elements.count == results.count will always evaluate to false because elements only contains successfully decoded values while results contains both successes and failures. This means the outcome will always be .recoveredFrom even when all elements decode successfully.

 #if DEBUG
-if elements.count == results.count {
+let hasAnyFailure = results.contains { result in
+  if case .failure = result { return true }
+  return false
+}
+if !hasAnyFailure {
   self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
 } else {
   let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
   self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
 }
 #else

Likely an incorrect or invalid review comment.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1)

1-162: Excellent comprehensive test coverage for PolymorphicArrayValue resilient decoding!

The test suite thoroughly validates all key scenarios:

  • Empty array handling with correct outcome
  • Successful heterogeneous polymorphic decoding
  • Proper failure propagation for invalid elements
  • Missing key and type mismatch error handling
  • Integration with the error reporting system

The use of DummyNotice types provides realistic polymorphic testing scenarios, and the conditional DEBUG compilation for projected value testing is properly implemented.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (2)

12-29: LGTM! Well-structured resilient decoding implementation.

The decode method properly handles missing keys and null values by returning appropriate outcomes. The implementation follows a clear pattern of checking key existence first, then null values, before attempting actual decoding.


31-48: Clean implementation of optional decoding semantics.

The decodeIfPresent method correctly distinguishes between missing keys (returns nil) and null values (returns empty array with .valueWasNil outcome), maintaining consistency with Swift's decoding conventions.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2)

68-69: Proper error reporting implementation.

Good practice to report the error before rethrowing. This ensures errors are captured for debugging while maintaining the expected decoding failure behavior.


52-56: Well-implemented debug diagnostics.

The conditional compilation ensures debugging information is only available in debug builds, maintaining optimal performance in release while providing valuable diagnostics during development.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (2)

77-99: Comprehensive null value handling test.

Good test coverage for the null value case. The use of confirmation pattern ensures the test properly validates that decoding throws as expected.


101-142: Excellent error reporting integration test.

The test thoroughly validates error reporting functionality, including proper handling of debug vs release builds. Good use of the error reporter's flush mechanism to verify errors were captured.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)

12-21: Clean delegation pattern.

The decode method properly delegates to decodeIfPresent and handles the missing key case with the appropriate outcome.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (3)

27-42: Consistent resilient decoding implementation.

The outcome tracking and projected value implementation follows the established pattern across all property wrappers. The debug-only exposure of decoding outcomes provides valuable diagnostics without impacting production performance.


51-61: Robust error handling for polymorphic decoding.

Good implementation that captures decoding errors through the error reporter while preserving the throwing behavior. This is particularly important for polymorphic types where type identification or decoding might fail.


63-73: Proper protocol conformances.

The explicit Equatable and Hashable implementations correctly delegate to the wrapped value, following Swift best practices.

Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (4)

28-28: LGTM! Outcome tracking property added correctly.

The outcome property properly tracks decoding results using ResilientDecodingOutcome and integrates well with the resilient decoding pattern.


35-38: Good addition of internal initializer for outcome control.

The internal initializer allows setting both wrapped value and outcome, which is useful for testing and internal framework usage.


40-42: Projected value implementation follows best practices.

The debug-only projected value properly exposes decoding outcome for diagnostics while being unavailable in release builds.


66-66: Nice simplification of equality operator.

Using Self instead of explicit generic parameters makes the code cleaner and more maintainable.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (6)

5-9: Excellent test fixture design.

The Fixture struct with @DummyNotice.OptionalPolymorphic property wrapper provides a clean test setup for verifying resilient decoding behavior.


11-30: Comprehensive null value testing.

The test properly verifies that null values result in nil wrapped value and .valueWasNil outcome, with appropriate debug-only assertions.


32-49: Good coverage of missing key scenario.

Test correctly validates that missing keys result in nil wrapped value and .keyNotFound outcome.


51-80: Thorough successful decoding validation.

The test verifies successful polymorphic decoding with proper type checking and outcome validation. The use of #require for unwrapping is appropriate.


82-99: Proper error handling test for unknown types.

Test correctly expects errors to be thrown for unknown polymorphic types, which aligns with the expected behavior.


101-115: Good invalid JSON format coverage.

Test appropriately verifies that invalid JSON types throw errors during decoding.

Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (4)

28-28: Outcome tracking properly implemented.

The ResilientDecodingOutcome property correctly tracks decoding results following the established pattern.


35-38: Internal initializer provides good flexibility.

The internal initializer with outcome parameter enables precise control for testing and framework usage.


40-42: Debug-only projected value follows framework pattern.

Consistent implementation with other property wrappers for debugging support.


69-79: Explicit protocol conformances improve clarity.

The explicit Equatable and Hashable implementations make the behavior clear and provide better control over comparison logic.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (4)

23-24: Outcome tracking properly added.

The ResilientDecodingOutcome property correctly integrates with the resilient decoding infrastructure.


32-35: Internal initializer provides good control.

The internal initializer enables precise outcome setting for testing and framework usage.


61-61: Outcome correctly set on successful decoding.

The assignment of .decodedSuccessfully properly tracks successful array decoding.


74-84: Explicit protocol conformances improve maintainability.

The explicit Equatable and Hashable implementations provide clear behavior and better control.

Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (4)

34-35: Outcome tracking properly implemented.

The ResilientDecodingOutcome property correctly supports resilient decoding with detailed tracking.


42-45: Internal initializer enables precise control.

Good addition for testing and framework usage with explicit outcome setting.


74-77: Excellent improvement in error reporting.

Replacing print statements with decoder.reportError(error) provides proper error reporting integration with the resilient decoding infrastructure. The .recoveredFrom(error, wasReported: true) outcome correctly reflects the error recovery.


91-101: Explicit protocol conformances enhance clarity.

The explicit Equatable and Hashable implementations provide clear behavior and better maintainability compared to empty conformances.

Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)

12-28: Well-structured error container for array decoding failures [HIGH]

The ArrayDecodingError struct provides a clean way to track individual element decoding results. The use of Result<Element, Error> array and the computed errors property for extracting failures is an elegant design choice.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1)

1-168: Excellent test coverage for LossyArray resilient decoding [HIGH]

The test suite comprehensively covers partial failures, error reporting, and edge cases. The Result extension utility is a nice addition for test assertions.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2)

65-74: Correct error handling for non-resilient wrapper [HIGH]

The implementation correctly re-throws errors without recovery, which aligns with the documented behavior of OptionalPolymorphicValue as a non-resilient wrapper. This distinguishes it from LossyOptionalPolymorphicValue which provides error tolerance.


76-86: Proper Equatable and Hashable implementations [MEDIUM]

The implementations correctly base equality and hashing solely on the wrappedValue, ignoring the outcome property. This ensures that two instances with the same value but different decoding outcomes are considered equal, which is the expected behavior.

Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1)

45-58: Elegant use of dynamic member lookup for error access [MEDIUM]

The @dynamicMemberLookup implementation for array and dictionary projected values provides intuitive access to detailed error information through computed properties, making the debugging experience more ergonomic.

Also applies to: 64-77

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2)

40-62: LGTM! Well-structured resilient decoding support

The addition of the outcome property and DEBUG-only projectedValue follows the established pattern across other property wrappers. The internal initializer provides the necessary flexibility for setting outcomes during decoding.


121-133: LGTM! Correct conformance implementations

The Equatable and Hashable implementations correctly focus on the wrapped value only, consistent with other property wrappers in the codebase. The outcome is appropriately excluded from equality checks as it's purely diagnostic information.

Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2)

45-56: Good defensive programming with clear error messaging

The dual projectedValue implementation effectively prevents accidental usage in production builds while providing helpful diagnostics in DEBUG builds. The fatalError message clearly indicates this is a programmer error.


67-80: LGTM! Clean lossy decoding implementation

The error handling correctly implements the lossy pattern - any decoding error results in nil while properly reporting the error for diagnostics. The outcome tracking with wasReported: true accurately reflects that the error was captured.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (3)

14-32: Well-designed test fixture covering edge cases

Good test design that covers various types and includes a non-convertible NestedObject to test the lossless filtering behavior. The fixture effectively tests the resilient decoding across different data types.


34-69: Comprehensive test coverage for projected value diagnostics

Excellent test that validates both the lossless filtering behavior and the error tracking through projected values. The test data includes a good mix of valid and invalid elements for each type.


101-143: Good coverage of failure scenarios

The tests correctly verify that @LosslessArray (being non-optional) throws errors for missing keys and non-array values. This distinguishes it from optional variants that would handle these cases gracefully.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2)

12-25: Clean implementation with proper outcome tracking

The decode method correctly delegates to decodeIfPresent and sets the appropriate .keyNotFound outcome when the key is missing. This maintains consistency with the optional array semantics.


27-67: Well-structured decoding with proper error context

Good implementation that handles all cases (missing key, nil value, valid array, errors). The use of superDecoder(forKey:) for error reporting ensures errors are reported with the correct coding path context.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (3)

15-28: Well-structured test fixture using real polymorphic types

Good use of @CustomCodable with snake_case and multiple properties to test isolation of outcomes. Using the actual DummyNotice polymorphic type provides realistic test coverage.


139-188: Thorough error reporting validation

Excellent tests that verify OptionalPolymorphicArrayValue fails fast on invalid elements (as designed) and properly integrates with the error reporting system. This distinguishes it from lossy variants that would skip invalid elements.


252-286: Excellent integration test for outcome isolation

This test effectively validates that each property maintains its own outcome state independently. Great coverage of all possible states (successful, nil, empty, missing) in a single test case.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)

72-73: Translate Korean comment to English [LOW]

For consistency with the rest of the codebase, please translate the Korean comment.

-    // Release 빌드에서는 성능 최적화를 위해 에러 정보를 반환하지 않음
+    // In release builds, error information is not returned for performance optimization
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (1)

274-286: Good implementation of error creation helper [LOW]

Great job extracting the error creation logic into a reusable helper method. This improves code maintainability and addresses the previous review feedback effectively.

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1)

217-423: Excellent test coverage for RawRepresentable support [HIGH]

The comprehensive test suite for RawRepresentable types with frozen/non-frozen strategies is well-designed and provides excellent coverage of:

  • Valid raw values
  • Unknown raw values with different error behaviors
  • Missing keys and null values
  • Type mismatches

This thoroughly validates the new resilient decoding features.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (4)

16-36: Well-structured resilient decoding implementation! [HIGH]

The property wrapper properly implements outcome tracking with appropriate initializers and DEBUG-conditional projected value exposure.


135-183: Force cast issue properly resolved with safe casting! [HIGH]

Great job addressing the previous review feedback. The implementation now uses safe casting with guard let statements (lines 142, 167) instead of force casts, preventing potential runtime crashes.


198-234: Robust decoding implementation with comprehensive error handling! [HIGH]

The decoding logic properly handles nil values, supports both String and Int keys, and integrates well with the error reporting system. The separation of concerns makes the code maintainable.


268-280: Clean KeyedDecodingContainer extension! [MEDIUM]

The extension provides a convenient way to decode LossyDictionary values with proper handling of missing keys and outcome tracking.

Comment on lines +35 to +37
case let .recoveredFrom(error as ArrayDecodingError, wasReported):
assert(!wasReported)
return error
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace assert with proper error handling [HIGH]

The assert(!wasReported) on line 36 could crash the app in DEBUG builds. Consider handling this case more gracefully or documenting why this condition should never occur.

Consider this alternative approach:

-    case let .recoveredFrom(error as ArrayDecodingError, wasReported):
-      assert(!wasReported)
-      return error
+    case let .recoveredFrom(error as ArrayDecodingError, wasReported):
+      // If the error was already reported, we can still return it for diagnostic purposes
+      // Consider logging this unexpected state if it indicates a logic error
+      return error

Or if this truly represents an invalid state, add clear documentation:

     case let .recoveredFrom(error as ArrayDecodingError, wasReported):
+      // This should never happen because ArrayDecodingError is only created internally
+      // and we ensure it's never reported to maintain error aggregation integrity
       assert(!wasReported)
       return error
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case let .recoveredFrom(error as ArrayDecodingError, wasReported):
assert(!wasReported)
return error
case let .recoveredFrom(error as ArrayDecodingError, wasReported):
// If the error was already reported, we can still return it for diagnostic purposes
// Consider logging this unexpected state if it indicates a logic error
return error
🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift around lines 35
to 37, replace the assert(!wasReported) with proper error handling to avoid
crashing the app in DEBUG builds. Either handle the case gracefully by returning
a default error or logging the unexpected state, or add clear documentation
explaining why this condition should never occur and why an assert is justified.

Comment on lines +30 to +41
func dictionaryDecodingError<K: Hashable, V>() -> ResilientDecodingOutcome.DictionaryDecodingError<K, V> {
typealias DictionaryDecodingError = ResilientDecodingOutcome.DictionaryDecodingError<K, V>
switch self {
case .decodedSuccessfully, .keyNotFound, .valueWasNil:
return .init(results: [:])
case let .recoveredFrom(error as DictionaryDecodingError, wasReported):
assert(!wasReported)
return error
case .recoveredFrom(_, _):
return .init(results: [:])
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the wasReported assertion logic [HIGH]

The assertion at line 36 assumes wasReported is always false for DictionaryDecodingError. This could cause debug crashes if the assumption is violated.


🏁 Script executed:

#!/bin/bash
# Search for usage patterns of DictionaryDecodingError to verify the wasReported flag handling
ast-grep --pattern 'DictionaryDecodingError($$$)'

# Check how recoveredFrom is used with DictionaryDecodingError
rg -A 5 "recoveredFrom.*DictionaryDecodingError" 

# Look for similar patterns in ArrayDecodingError for consistency
rg -A 10 "assert.*wasReported" Sources/KarrotCodableKit/Resilient/

Length of output: 2036


Review the wasReported assertion in DictionaryDecodingError

The assert(!wasReported) at line 36 will crash in debug builds if the wasReported flag ever becomes true. Although this matches the pattern in ArrayDecodingError.swift, relying on this invariant without documentation or a fallback risks unexpected test‐time failures.

Suggestions:

  • Confirm and document the invariant that wasReported is always false when entering this case.
  • If wasReported might ever be true, handle it more gracefully (e.g. log and continue, or fall back to an empty result).
  • Alternatively, replace the assert with a non‐crashing guard or comment the rationale clearly so future maintainers understand the guarantee.

Location:

  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift, line 36
🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift around lines
30 to 41, the assert(!wasReported) at line 36 can cause crashes in debug builds
if wasReported is true. To fix this, either add clear documentation explaining
why wasReported must always be false here, or replace the assert with a
non-crashing guard that logs the unexpected true case and returns a safe default
result. This ensures safer handling and clearer intent for future maintainers.

ElonPark added 8 commits July 29, 2025 22:13
- Add outcome tracking to LosslessValue and LosslessArray
- Implement projected value for error reporting
- Handle decoding errors with proper error tracking
- Add comprehensive tests for resilient behavior
- Add outcome tracking to LossyArray and LossyDictionary
- Implement projected value for error reporting
- Track element-level decoding results
- Add comprehensive tests for resilient behavior
- Add outcome tracking to PolymorphicValue and PolymorphicArrayValue
- Implement projected value for error reporting
- Handle decoding errors with proper error tracking
- Add comprehensive tests for resilient behavior
- Add outcome tracking to OptionalPolymorphicValue
- Update KeyedDecodingContainer extension
- Implement projected value for error reporting
- Add comprehensive tests for resilient behavior
- Add outcome tracking to LossyOptionalPolymorphicValue
- Update KeyedDecodingContainer extension
- Implement projected value for error reporting
- Add comprehensive tests for resilient behavior
- Add outcome tracking to OptionalPolymorphicArrayValue
- Update KeyedDecodingContainer extension with error handling
- Implement projected value for error reporting
- Add comprehensive tests for resilient behavior
…alue

- Add outcome tracking to DefaultEmptyPolymorphicArrayValue
- Update KeyedDecodingContainer extension with error handling
- Implement projected value for error reporting
- Move test file to ArrayValue directory
- Add outcome tracking to PolymorphicLossyArrayValue
- Update KeyedDecodingContainer extension with error handling
- Implement projected value with element results
- Move test file to ArrayValue directory
@ElonPark ElonPark force-pushed the feature/elon/add-resilient-decoding branch from 3eb4df4 to 38097d1 Compare July 29, 2025 13:49
Copy link
Contributor

@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: 10

🔭 Outside diff range comments (1)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

50-63: Add error reporting for consistency with other property wrappers [HIGH]

The Decodable implementation doesn't report errors when decoding fails, unlike other property wrappers in this PR. Consider adding error reporting:

 extension PolymorphicArrayValue: Decodable {
   public init(from decoder: Decoder) throws {
-    var container = try decoder.unkeyedContainer()
-
-    var elements = [PolymorphicType.ExpectedType]()
-    while !container.isAtEnd {
-      let value = try container.decode(PolymorphicValue<PolymorphicType>.self).wrappedValue
-      elements.append(value)
-    }
-
-    self.wrappedValue = elements
-    self.outcome = .decodedSuccessfully
+    do {
+      var container = try decoder.unkeyedContainer()
+      
+      var elements = [PolymorphicType.ExpectedType]()
+      while !container.isAtEnd {
+        let value = try container.decode(PolymorphicValue<PolymorphicType>.self).wrappedValue
+        elements.append(value)
+      }
+      
+      self.wrappedValue = elements
+      self.outcome = .decodedSuccessfully
+    } catch {
+      decoder.reportError(error)
+      throw error
+    }
   }
 }
♻️ Duplicate comments (24)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (1)

58-76: Consider differentiating between direct and fallback decoding outcomes [MEDIUM]

Both successful direct decoding and fallback decoding set the outcome to .decodedSuccessfully. For better debugging insights, consider using a different outcome like .recoveredFrom(error) for the fallback case to indicate that type conversion was necessary.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1)

47-75: Consider expecting a different outcome for fallback decoding [MEDIUM]

The test currently expects .decodedSuccessfully when an unknown type triggers fallback behavior. A more specific outcome like .recoveredFrom() would provide better debugging information to distinguish between perfect decoding and fallback scenarios.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

43-46: Enhance fatal error message for production builds

The error message could be more informative about why this is a programmer error.

   public var projectedValue: Never {
-    fatalError("@\(Self.self) projectedValue should not be used in non-DEBUG builds")
+    fatalError("@\(Self.self) projectedValue is only available in DEBUG builds for decoding diagnostics. Remove usage before shipping to production.")
   }
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)

46-60: Simplify nested error handling

Both catch blocks perform identical error reporting and rethrowing. Consider simplifying to reduce redundancy.

   public init(from decoder: Decoder) throws {
     do {
       let stringValue = try String(from: decoder)
-      do {
-        self.wrappedValue = try Coder.decode(stringValue)
-        self.outcome = .decodedSuccessfully
-      } catch {
-        decoder.reportError(error)
-        throw error
-      }
+      self.wrappedValue = try Coder.decode(stringValue)
+      self.outcome = .decodedSuccessfully
     } catch {
       decoder.reportError(error)
       throw error
     }
   }
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)

38-47: Optimize superDecoder usage and improve error handling

Creating superDecoder twice is inefficient, and the error reporting could fail silently if the second decoder creation fails.

     // Try to decode the polymorphic value
+    let decoder = try superDecoder(forKey: key)
     do {
-      let decoder = try superDecoder(forKey: key)
       let value = try T.decode(from: decoder)
       return LossyOptionalPolymorphicValue(wrappedValue: value, outcome: .decodedSuccessfully)
     } catch {
       // Report error to resilient decoding error reporter
-      let decoder = try? superDecoder(forKey: key)
-      decoder?.reportError(error)
+      decoder.reportError(error)
       return LossyOptionalPolymorphicValue(wrappedValue: nil, outcome: .recoveredFrom(error, wasReported: true))
     }
Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)

30-41: Review the wasReported assertion in DictionaryDecodingError [HIGH]

The assert(!wasReported) at line 36 will crash in debug builds if the wasReported flag ever becomes true. Although this matches the pattern in ArrayDecodingError.swift, relying on this invariant without documentation or a fallback risks unexpected test‐time failures.

Suggestions:

  • Confirm and document the invariant that wasReported is always false when entering this case.
  • If wasReported might ever be true, handle it more gracefully (e.g. log and continue, or fall back to an empty result).
  • Alternatively, replace the assert with a non‐crashing guard or comment the rationale clearly so future maintainers understand the guarantee.
Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (1)

47-57: Problematic fatalError in production builds! [HIGH]

The fatalError in non-DEBUG builds could crash production apps if the projected value is accidentally accessed. Consider using a compile-time approach instead.

-  #else
-  /// In non-DEBUG builds, accessing projectedValue is a programmer error  
-  public var projectedValue: Never {
-    fatalError("@\(Self.self) projectedValue should not be used in non-DEBUG builds")
-  }
-  #endif
+  #endif

This removes the fatalError completely in non-DEBUG builds, making it a compile-time error instead of a runtime crash.

Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)

47-64: Simplify nested error handling structure

The inner try-catch block only reports and rethrows the error, which duplicates the outer catch behavior.

   public init(from decoder: Decoder) throws {
     do {
       let value = try Formatter.RawValue(from: decoder)
-      do {
-        self.wrappedValue = try Formatter.decode(value)
-        self.outcome = .decodedSuccessfully
-      } catch {
-        decoder.reportError(error)
-        throw error
-      }
+      self.wrappedValue = try Formatter.decode(value)
+      self.outcome = .decodedSuccessfully
     } catch DecodingError.valueNotFound(let rawType, _) where rawType == Formatter.RawValue.self {
       self.wrappedValue = nil
       self.outcome = .valueWasNil
     } catch {
       decoder.reportError(error)
       throw error
     }
   }
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1)

6-6: Consider shortening the type name to comply with SwiftLint rules [LOW]

The type name LossyOptionalPolymorphicValueResilientTests is 46 characters, exceeding the 40-character limit. Consider shortening it while maintaining clarity.

-struct LossyOptionalPolymorphicValueResilientTests {
+struct LossyOptionalPolymorphicResilientTests {
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)

71-85: Inconsistent error reporting flag

Individual element errors are reported via elementDecoder.reportError(error), but the aggregate error is marked as wasReported: false.

       } else {
         let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
-        self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
+        self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: true))
       }
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)

47-58: Consider memory implications for large arrays [MEDIUM]

The PolymorphicLossyArrayProjectedValue stores a results array with individual Result<T, Error> entries for each element. For large arrays, this could consume significant memory in DEBUG builds.

Consider adding a configurable limit on tracked results:

public struct PolymorphicLossyArrayProjectedValue<T>: PolymorphicProjectedValueProtocol {
  /// The outcome of the decoding process
  public let outcome: ResilientDecodingOutcome
  
  /// Results of decoding each element in the array (limited for memory efficiency)
  public let results: [Result<T, Error>]
  
  /// Maximum number of individual results to track
  private static var maxTrackedResults = 1000
  
  public init(outcome: ResilientDecodingOutcome, results: [Result<T, Error>]) {
    self.outcome = outcome
    // Limit results to prevent memory issues with very large arrays
    self.results = Array(results.prefix(Self.maxTrackedResults))
  }
}
Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (3)

34-34: Translate Korean comments to English [LOW]

Please translate the Korean comments to English:

  • Line 34: // 기본 동작 검증// Verify basic behavior
  • Line 40: // projected value로 성공 정보 접근// Access success information through projected value

Also applies to: 40-40


65-65: Fix incorrect comment referencing Base64 [LOW]

The comment mentions "Invalid Base64 format" but this test is about date parsing, not Base64 conversion.

-        // Invalid Base64 format causes decoding failure
+        // Invalid date format causes decoding failure

89-89: Fix incorrect comment referencing Data [LOW]

The comment mentions "null values cannot be converted to Data" but this test is about date parsing.

-        // null values cannot be converted to Data
+        // null values cannot be converted to Date
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)

35-37: Replace assert with proper error handling [HIGH]

The assert(!wasReported) on line 36 could crash the app in DEBUG builds. Consider handling this case more gracefully or documenting why this condition should never occur.

Consider this alternative approach:

-    case let .recoveredFrom(error as ArrayDecodingError, wasReported):
-      assert(!wasReported)
-      return error
+    case let .recoveredFrom(error as ArrayDecodingError, wasReported):
+      // If the error was already reported, we can still return it for diagnostic purposes
+      // Consider logging this unexpected state if it indicates a logic error
+      return error

Or if this truly represents an invalid state, add clear documentation:

     case let .recoveredFrom(error as ArrayDecodingError, wasReported):
+      // This should never happen because ArrayDecodingError is only created internally
+      // and we ensure it's never reported to maintain error aggregation integrity
       assert(!wasReported)
       return error
Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1)

11-27: Consider protocol unification to reduce duplication [MEDIUM]

The ResilientProjectedValueProtocol is nearly identical to PolymorphicProjectedValueProtocol in the PolymorphicCodable module. While the separation might serve module boundaries, consider if a shared base protocol could reduce code duplication while maintaining clear architectural separation.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (1)

66-103: Consider the multiple container access compatibility issue [HIGH]

The past review comment raises a valid concern about decoder compatibility. The current implementation first accesses singleValueContainer() to check for nil, then switches to unkeyedContainer() for array decoding. While this works with standard JSONDecoder, custom decoders might not support multiple container accesses.

The suggested refactor to decode [PolymorphicValue<PolymorphicType>] directly from the single-value container would:

  • Improve compatibility with custom decoders
  • Simplify the implementation
  • Maintain consistency with other polymorphic wrappers

This is a runtime correctness issue that could affect users with custom decoder implementations.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)

59-63: Fix DEBUG vs release initialization inconsistency [HIGH]

The null handling in decodeIfPresent has different initializer signatures between DEBUG and release builds. The release path is missing the outcome parameter, causing inconsistent behavior.

    if try decodeNil(forKey: key) {
      #if DEBUG
      return PolymorphicLossyArrayValue(wrappedValue: [], outcome: .valueWasNil, results: [])
      #else
-     return PolymorphicLossyArrayValue(wrappedValue: [])
+     return PolymorphicLossyArrayValue(wrappedValue: [], outcome: .valueWasNil)
      #endif
    }
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (1)

6-6: Shorten test suite name to comply with linting rules [LOW]

The type name exceeds SwiftLint's 40-character limit (currently 47 characters).

-struct DefaultEmptyPolymorphicArrayValueResilientTests {
+struct DefaultEmptyArrayResilientTests {
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1)

117-134: Simplify result assertions with helper extension [LOW]

The current if-case pattern with Issue.record is verbose and could be simplified.

Add this extension to improve readability:

extension Result {
  var isSuccess: Bool {
    if case .success = self { return true }
    return false
  }
  
  var isFailure: Bool {
    !isSuccess
  }
}

Then simplify the assertions:

-    if case .success = result.$notices.results[0] {} else {
-      Issue.record("Expected success at index 0")
-    }
+    #expect(result.$notices.results[0].isSuccess)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (2)

46-56: Consider memory limits for large arrays in DEBUG mode [MEDIUM]

Storing results for every array element could cause memory issues with large arrays in DEBUG builds.

Add a configurable limit to prevent excessive memory usage:

#if DEBUG
/// Maximum number of individual results to track (prevents memory issues with large arrays)
private static let maxTrackedResults = 1000

/// Results of decoding each element in the array (DEBUG only)
let results: [Result<PolymorphicType.ExpectedType, Error>]
#endif

Then in the decoder, limit result tracking:

#if DEBUG
if results.count < Self.maxTrackedResults {
    results.append(.success(value))
}
#endif

115-121: Consider reflecting partial failures in outcome [MEDIUM]

The outcome is always .decodedSuccessfully even when elements fail to decode. While the lossy behavior is correct, consider whether partial success should be reflected.

Track whether any elements failed and set appropriate outcome:

self.wrappedValue = elements
#if DEBUG
let hasFailures = results.contains { if case .failure = $0 { return true }; return false }
self.outcome = hasFailures ? .decodedWithPartialErrors : .decodedSuccessfully
self.results = results
#else
// In release, we can't track individual failures efficiently
self.outcome = .decodedSuccessfully
#endif

Note: This would require adding a new .decodedWithPartialErrors case to ResilientDecodingOutcome or using .recoveredFrom with a composite error.

Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (2)

18-31: Document the Equatable behavior for error comparison [LOW]

The Equatable implementation ignores the actual error value in the recoveredFrom case, comparing only the wasReported flag. While this is a reasonable design choice, it should be documented to prevent confusion.


32-40: Remove unused recoveredFromDebugOnlyError property [MEDIUM]

The static property recoveredFromDebugOnlyError on line 37 is declared but never used. The recoveredFrom method always returns a new instance instead of using this static property.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb4df4 and 38097d1.

📒 Files selected for processing (45)
  • README.md (1 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (6 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (3 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (4 hunks)
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (3 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (4 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (3 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift (5 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
*

⚙️ CodeRabbit Configuration File

*: You are a senior Swift/iOS engineer with 15+ years of experience reviewing KarrotCodableKit code changes.

Review Priorities [HIGH]

  • Code quality: memory leaks, error handling, readability (100 chars/line)
  • Architecture: SOLID principles, design patterns, DRY/KISS
  • Swift best practices: modern features, memory management, performance

Communication

  • Mark priority: [HIGH], [MEDIUM], [LOW]
  • Provide specific code examples
  • Focus on high-impact improvements over style

Files:

  • README.md
**/*.md

⚙️ CodeRabbit Configuration File

**/*.md: You are a senior technical writer reviewing documentation for KarrotCodableKit, a Swift Codable extension library.

1. Technical Accuracy [HIGH]

  • Verify code examples compile and work correctly
  • Check that API documentation matches actual implementation
  • Validate macro usage examples demonstrate proper syntax
  • Assess JSON examples are well-formed and realistic

2. Documentation Completeness [HIGH]

  • Review coverage of all major features (CustomCodable, PolymorphicCodable, AnyCodable, BetterCodable)
  • Check that complex concepts like polymorphic type resolution are well explained
  • Verify installation and setup instructions are current
  • Assess troubleshooting and error handling guidance

3. User Experience [MEDIUM]

  • Evaluate documentation structure and navigation clarity
  • Check that examples progress from simple to complex appropriately
  • Verify code snippets include necessary imports and context
  • Assess whether documentation answers common user questions

Review Focus

  • Prioritize accuracy and completeness over style
  • Focus on developer experience and practical usage scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • README.md
Sources/KarrotCodableKit/**

⚙️ CodeRabbit Configuration File

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
Tests/KarrotCodableKitTests/**

⚙️ CodeRabbit Configuration File

Tests/KarrotCodableKitTests/**: You are a senior Swift/iOS engineer reviewing runtime functionality tests for KarrotCodableKit's Codable extensions.

1. Runtime Behavior Testing [HIGH]

  • Verify JSON encoding/decoding scenarios cover real-world use cases
  • Check polymorphic type resolution testing across different strategies
  • Assess property wrapper testing (@DATEvalue, @DefaultFalse, etc.)
  • Validate error handling and edge case coverage

2. Test Data Quality [HIGH]

  • Review test JSON structures for realistic complexity
  • Check that TestDoubles provide comprehensive mock scenarios
  • Verify test data covers various data types and edge cases
  • Assess polymorphic test data represents actual usage patterns

3. Integration Testing [MEDIUM]

  • Check integration between macro-generated code and runtime functionality
  • Verify end-to-end scenarios combining multiple KarrotCodableKit features
  • Assess performance testing for large data structures
  • Review memory usage testing for complex polymorphic scenarios

Review Focus

  • Prioritize real-world usage scenario coverage
  • Focus on Codable correctness and performance implications
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
🧬 Code Graph Analysis (13)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (2)
Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (2)
  • lhs (92-94)
  • hash (98-100)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2)
  • lhs (64-66)
  • hash (70-72)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (2)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)
  • decodeIfPresent (48-69)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (8)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)
  • lhs (107-109)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (1)
  • lhs (259-261)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (1)
  • lhs (86-88)
Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (1)
  • lhs (66-68)
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)
  • lhs (70-72)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)
  • lhs (75-77)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (1)
  • lhs (99-101)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (2)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicValueTests.swift (1)
  • OptionalPolymorphicValueTests (13-71)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (3)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
  • arrayDecodingError (30-41)
Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)
  • dictionaryDecodingError (30-41)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (1)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (2)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
  • decodeIfPresent (31-48)
Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (3)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
🪛 SwiftLint (0.57.0)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift

[Warning] 16-16: Type name 'ResilientOptionalPolymorphicArrayDummyResponse' should be between 3 and 40 characters long

(type_name)


[Warning] 30-30: Type name 'OptionalPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift

[Warning] 6-6: Type name 'LossyOptionalPolymorphicValueResilientTests' should be between 3 and 40 characters long

(type_name)

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift

[Warning] 6-6: Type name 'DefaultEmptyPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Test (16.1, debug)
  • GitHub Check: Build and Test (16.1, release)
  • GitHub Check: Check Macro Compatibility
🔇 Additional comments (63)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1)

164-164: LGTM! Nice attention to detail.

Good catch on the grammatical correction - maintaining clean and professional comments contributes to overall code quality.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1)

120-120: LGTM! Consistent grammatical improvement.

Another good fix maintaining consistency across test comments.

README.md (1)

280-280: LGTM! Proper attribution for new feature.

Good practice to acknowledge the source of the ResilientDecodingOutcome concept. The addition follows the established pattern of other acknowledgments and properly credits the original work.

Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift (1)

59-59: Excellent safety improvement! Replacing force unwraps with proper error handling.

Great work replacing the force unwraps (Data(json.utf8)!) with the safer try #require(json.data(using: .utf8)) pattern. This prevents potential crashes in tests and provides better error diagnostics when string-to-data conversion fails, which aligns perfectly with the resilient decoding theme of this PR.

Also applies to: 85-85, 109-109, 128-128, 148-148

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (1)

116-116: LGTM! Nice cleanup of test comment formatting.

Good standardization of the test structure comments to the clean "// given", "// when", "// then" format. This improves consistency across the test suite and makes the test flow more readable.

Also applies to: 133-133, 136-136, 163-163

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)

12-21: Clean delegation pattern with proper outcome tracking.

The method correctly delegates to decodeIfPresent and explicitly sets the outcome to .keyNotFound when the key is missing, which aligns perfectly with the resilient decoding pattern introduced across the codebase.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (2)

19-37: Well-structured resilient decoding integration.

The property wrapper correctly implements the resilient decoding pattern with:

  • Appropriate outcome tracking
  • Clean separation between public and internal initializers
  • Debug-only projected value for error inspection

85-95: Appropriate equality semantics for collection wrapper.

The Equatable and Hashable implementations correctly focus on the wrapped array content rather than the decoding outcome, which aligns with expected collection behavior.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (2)

6-30: Well-structured test suite with proper debug build handling.

The test correctly uses the new Swift Testing framework and properly handles debug-only assertions for the projected value outcomes.


132-161: Comprehensive error reporting verification. [HIGH]

Excellent test that verifies the error reporting integration works correctly for polymorphic array decoding failures. The explanatory comment adds valuable context.

Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (1)

13-122: Excellent test coverage for DataValue resilient decoding. [HIGH]

The test suite comprehensively covers all decoding scenarios with proper:

  • Debug/release build differentiation
  • Error reporting verification
  • Use of Swift Testing's confirmation API for async assertions
  • Clear test names and structure
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (2)

12-29: Well-structured decode implementation with proper outcome tracking [HIGH]

The implementation correctly handles all edge cases with explicit checks for missing keys and null values, providing appropriate outcomes for each scenario. The use of superDecoder properly delegates to the property wrapper's decoder.


31-48: Correct optional decoding semantics [HIGH]

The decodeIfPresent implementation properly distinguishes between missing keys (returns nil) and null values (returns empty array with .valueWasNil outcome), which aligns with Swift's optional decoding conventions.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2)

34-56: Excellent resilient decoding infrastructure [HIGH]

The addition of outcome tracking, internal initializer for explicit outcome setting, and debug-only projected value follows best practices and maintains consistency with other property wrappers in the codebase.


91-93: Clean Equatable implementation using Self [LOW]

Good improvement using Self in the equality operator signature, which is more idiomatic and consistent with other property wrappers in the codebase.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (2)

101-142: Thorough error reporting test coverage [HIGH]

Excellent test implementation that verifies error reporting integration with JSONDecoder, properly uses flushReportedErrors(), and handles conditional compilation for debug/release modes.


77-99: Proper null value handling verification [HIGH]

Good test ensuring that PolymorphicValue correctly throws an error when encountering null values, as it's designed for non-optional polymorphic decoding.

Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (3)

46-56: Clean error handling implementation [HIGH]

Good implementation with a single do-catch block that properly reports errors before rethrowing. This addresses the previous review comment about avoiding nested try-catch structures.


27-42: Consistent resilient decoding pattern [HIGH]

The outcome tracking, internal initializer, and debug-only projected value follow the established pattern across all property wrappers, ensuring consistency in error diagnostics.


65-68: Idiomatic Equatable conformance [LOW]

Nice update to use Self in the equality operator, maintaining consistency with other property wrappers.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (3)

27-43: Well-implemented resilient decoding for polymorphic values [HIGH]

Excellent implementation of outcome tracking with proper initialization and debug-only projected value exposure. The use of PolymorphicProjectedValue suggests specialized debugging support for polymorphic types.


51-61: Robust error handling in decoding [HIGH]

The decoding implementation properly captures outcomes and reports errors through the centralized mechanism before rethrowing, enabling comprehensive error tracking.


63-73: Valuable Equatable and Hashable conformances [MEDIUM]

Good addition of explicit Equatable and Hashable conformances, which enhance the usability of PolymorphicValue in collections and comparisons while maintaining consistency with other property wrappers.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1)

1-30: Test structure and nil value handling look good!

The test suite structure follows best practices with clear test naming and proper use of the Swift Testing framework. The nil value test correctly verifies both the decoded result and the resilient decoding outcome.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (1)

74-84: Equatable and Hashable implementations look good!

The implementations correctly focus on the wrapped value only, excluding the decoding outcome metadata. This matches the pattern used in other property wrappers.

Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (3)

33-45: Implementation looks good!

The outcome property and initializers are well-designed. Making the outcome publicly readable while keeping the parameterized initializer internal provides a good balance between API transparency and encapsulation.


61-79: Excellent error handling implementation!

The decoding logic properly tracks outcomes and reports errors to the centralized error reporter. The use of decoder.reportError(error) with wasReported: true in the outcome ensures consistency in error tracking.


91-101: Proper Equatable and Hashable implementations!

Good decision to only compare/hash the wrappedValue and not the outcome. This ensures that two instances with the same data but different decoding paths are considered equal, which is the expected behavior.

Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)

28-43: Well-structured outcome tracking!

Great implementation of the outcome property and DEBUG-only projected value. Unlike some other wrappers, this correctly avoids runtime crashes in production by not providing the property at all in non-DEBUG builds.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1)

11-163: Excellent test coverage! [HIGH]

The test suite provides comprehensive coverage of resilient decoding scenarios including:

  • Nil values and missing keys
  • Successful decoding
  • Unknown types and invalid JSON
  • Error reporter integration

The consistent use of #if DEBUG ensures tests work correctly in both debug and release configurations.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)

39-54: Good null value detection pattern!

The approach of checking for null values first using singleValueContainer before attempting array decoding is a solid defensive programming practice. This ensures proper outcome tracking for null vs empty arrays.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1)

8-24: Well-designed test fixture!

The Fixture struct provides excellent coverage with different data types (integers, strings, and nested objects), making it easy to test various lossy decoding scenarios in a single pass.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)

19-28: Clean implementation of error extraction

The default implementation properly handles all ResilientDecodingOutcome cases and provides a consistent interface for error access.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (4)

29-31: Well-integrated outcome tracking [HIGH]

The addition of the outcome property properly implements resilient decoding tracking as per the PR objectives.


42-52: Proper DEBUG-only projected value implementation

The projected value correctly provides debug-only access to decoding outcomes while preventing usage in production builds.


64-74: Correct error handling for non-lossy wrapper [HIGH]

The implementation properly rethrows errors instead of recovering, which is the expected behavior for OptionalPolymorphicValue as opposed to its lossy counterpart.


76-86: Correct value semantics implementation

The Equatable and Hashable conformances properly consider only the wrapped value, excluding the diagnostic outcome information from value semantics.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (1)

41-61: Well-designed outcome tracking implementation [LOW]

The outcome tracking follows a consistent pattern with proper encapsulation:

  • Read-only outcome property ensures immutability
  • Debug-only projected value prevents production overhead
  • Clean separation between debug and release builds
Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (1)

73-78: Excellent error reporting integration [LOW]

The error handling properly integrates with the resilient decoding framework:

  • Reports errors for collection
  • Sets appropriate outcome with error tracking
  • Maintains lossy behavior by returning nil
Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (1)

19-32: Good test fixture design with unconvertible type [LOW]

The NestedObject type that always returns nil from init?(_ description:) effectively tests the lossless filtering behavior. This is a good pattern for testing conversion failures.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (1)

62-66: Error reporting via superDecoder is consistent and correct

I’ve checked the entire repo and confirmed:

  • Every polymorphic wrapper uses superDecoder(forKey:) (or container.superDecoder()) followed by reportError(error).
  • superDecoder preserves the full coding path, so reported errors point to the exact key or index that failed.
  • There’s no indication of side effects with standard decoders—just be sure any custom Decoder implementations forward userInfo[.resilientDecodingErrorReporter] and correctly implement superDecoder.

No changes needed here.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (1)

30-287: Excellent test coverage for resilient decoding [LOW]

This test suite provides comprehensive coverage of:

  • Success cases (valid arrays, empty arrays, null, missing keys)
  • Error cases with proper error reporting verification
  • Projected value behavior in debug builds
  • Multiple property interactions

The tests effectively validate real-world JSON processing scenarios.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (1)

12-137: Well-structured resilient decoding tests [HIGH]

The test suite provides comprehensive coverage for LosslessValue resilient decoding:

  • Successful conversion scenarios with projected value verification
  • Proper error handling for null and unconvertible values
  • Error reporting integration with JSONDecoder
  • Appropriate use of async confirmations for expected failures

The English comments make the test intentions clear.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (5)

16-36: LGTM! Well-structured resilient decoding support

The implementation properly adds outcome tracking and projected value support following the established pattern across the codebase. Good use of DEBUG conditional compilation to avoid runtime overhead in release builds.


76-134: Good refactoring into focused helper methods

The extraction of type-specific decoding methods improves readability and maintainability. The decision to skip non-integer keys (line 121) aligns well with the lossy decoding philosophy.


135-184: Excellent implementation of safe casting

Great job replacing the force casts with safe casting patterns. The guard-return approach (lines 142, 167) properly handles type mismatches without risking runtime crashes.


185-235: Well-structured error handling and state management

The implementation properly handles all edge cases including nil values and unsupported key types. Good separation between DEBUG and release behavior.


268-281: Clean KeyedDecodingContainer extension

The extension provides a consistent API for decoding LossyDictionary values with proper handling of missing keys.

Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (4)

14-29: Good protocol enhancement with backward compatibility

The isFrozen property addition provides valuable flexibility for handling unknown enum values while maintaining backward compatibility through the default implementation.


36-111: Consistent implementation of resilient decoding

The property wrapper properly implements outcome tracking and error reporting following the established patterns. Good addition of explicit Equatable and Hashable conformance.


157-209: Well-handled Bool decoding with error reporting

Good integration of error reporting while preserving the existing fallback behavior for Int and String to Bool conversions.


210-287: Excellent RawRepresentable handling with frozen enum support

Great implementation of the frozen enum concept. The extracted createUnknownRawValueError helper method improves readability as suggested in the previous review. The error handling provides clear context for debugging.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (4)

62-79: Clean error reporter implementation

Good use of the flush pattern for error collection. The hasErrors optimization prevents unnecessary object creation.


81-117: Well-designed hierarchical error storage

The Node-based structure efficiently organizes errors by coding path. Good design choice to support filtering of UnknownNovelValueError separately.


121-134: Clean Decoder extension for error reporting

Good defensive programming with proper nil checks and type verification.


138-183: Helpful DEBUG-only pretty printing

The formatted output with proper indentation and error type handling will be valuable for debugging. Good use of conditional compilation.

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (3)

178-178: Remove debug print statement [LOW]

Debug print statements can add noise to test output. Consider removing this line or converting it to a comment if the information is needed for documentation purposes.

-    print("Error digest: \(digest.debugDescription)")
+    // Error digest contains at least 3 type mismatch errors

184-215: Good coverage of LossyOptional behavior

The test properly validates that LossyOptional returns nil for invalid data while capturing error information in the projected value.


217-423: Excellent comprehensive testing of RawRepresentable support

The tests thoroughly cover all scenarios for frozen and non-frozen enums, including proper error type validation and error reporting integration. Great job testing the distinction between UnknownNovelValueError and DecodingError based on the isFrozen flag.

Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)

10-16: Well-designed enum for comprehensive outcome tracking [HIGH]

The enum cases effectively capture all possible decoding outcomes with appropriate error context. The Sendable conformance ensures thread safety for concurrent decoding scenarios.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (3)

12-24: Excellent test fixture design [HIGH]

The fixture provides comprehensive coverage with string keys, integer keys, and nested object values. Good practice implementing Equatable for test assertions.


86-115: Thorough error reporting validation [HIGH]

Good coverage of error reporting with proper conditional compilation handling. The test clearly validates that decoding errors are captured and reported through the error reporter.


117-164: Excellent edge case coverage [HIGH]

Both tests effectively validate the fallback behavior when dictionary decoding fails completely or keys are missing. The distinction between null values (not an error) and type mismatches (error) is properly tested.

Comment on lines +67 to +72
if elements.count == results.count {
self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
} else {
let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect outcome determination logic. [HIGH]

The condition elements.count == results.count will always be true since both arrays are appended together. You should check if any decoding failed instead.

 #if DEBUG
-if elements.count == results.count {
+if results.allSatisfy({ if case .success = $0 { true } else { false } }) {
   self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
 } else {
   let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
   self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if elements.count == results.count {
self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
} else {
let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
}
if results.allSatisfy({ if case .success = $0 { true } else { false } }) {
self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
} else {
let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
}
🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
around lines 67 to 72, the logic incorrectly compares elements.count and
results.count, which are always equal. Instead, update the condition to check if
any decoding failures occurred by inspecting the results array for errors. Use
this check to determine whether to initialize with .decodedSuccessfully or
.recoveredFrom error outcome accordingly.

Comment on lines +45 to 56
#if DEBUG
/// The projected value providing access to decoding outcome
public var projectedValue: PolymorphicProjectedValue {
PolymorphicProjectedValue(outcome: outcome)
}
#else
/// In non-DEBUG builds, accessing projectedValue is a programmer error
public var projectedValue: Never {
fatalError("@\(Self.self) projectedValue should not be used in non-DEBUG builds")
}
#endif
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider alternative to fatal error in production [MEDIUM]

While the fatal error prevents misuse of debug-only features in production, it could cause crashes if developers accidentally leave debug code in place. Consider:

  1. Using @available(*, unavailable) attribute in release builds
  2. Returning a stub value instead of crashing
  3. Using compile-time checks to prevent access

The current approach is valid but could be more developer-friendly.

🤖 Prompt for AI Agents
In
Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
around lines 45 to 56, the projectedValue property uses fatalError in non-DEBUG
builds, which can cause crashes if accessed accidentally. To fix this, replace
the fatalError implementation with the @available(*, unavailable) attribute on
the projectedValue property in release builds to prevent its usage at compile
time, or alternatively provide a stub value that safely indicates unavailability
without crashing. This change will make the code safer and more
developer-friendly by avoiding runtime crashes.

Comment on lines 159 to 158
// Result extension for testing
extension Result {
var isFailure: Bool {
switch self {
case .success: return false
case .failure: return true
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Useful test utility extension!

The isFailure computed property on Result improves test readability. Consider moving this to a shared test utilities file if other test suites could benefit from it.

🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
around lines 159 to 167, the isFailure computed property on Result is a useful
test utility. To improve code reuse and maintainability, move this extension to
a shared test utilities file that can be imported by multiple test suites
instead of keeping it local to this test file.

Comment on lines +35 to +56
"intDict": {
"1": "first",
"2": "second",
"3": "third",
"invalid": "should be ignored"
},
"objectDict": {
"obj1": {"id": 1, "name": "first"},
"obj2": {"id": "invalid", "name": "second"},
"obj3": {"id": 3, "name": "third"}
}
}
"""

let decoder = JSONDecoder()
let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(Fixture.self, from: data)

// Verify default behavior - only valid key-value pairs included
#expect(fixture.stringDict == ["one": 1, "three": 3])
#expect(fixture.intDict == [1: "first", 2: "second", 3: "third"]) // All Int keys are valid
#expect(fixture.objectDict == [
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify integer dictionary key parsing behavior [HIGH]

The test JSON includes "invalid": "should be ignored" in the intDict (line 39), but the assertion expects all keys to be valid (line 55) with comment "All Int keys are valid". This seems inconsistent.

Please verify whether non-integer string keys in JSON are silently ignored when decoding to [Int: String] dictionaries, or if this test data needs correction.


🏁 Script executed:

#!/bin/bash
# Search for LossyDictionary implementation to understand key parsing behavior
ast-grep --pattern 'extension LossyDictionary: Decodable {
  $$$
}'

# Look for integer key parsing logic
rg -A 10 "Int.*CodingKey" 

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Locate where LossyDictionary is defined in the codebase
rg -n "LossyDictionary" .

# Find the Decodable conformance for LossyDictionary
rg -n "extension.*LossyDictionary.*Decodable" .

# Show 10 lines of context around that conformance to inspect key-parsing logic
rg -C 10 "extension.*LossyDictionary.*Decodable" .

Length of output: 7008


🏁 Script executed:

#!/bin/bash
# Display the Decodable implementation of LossyDictionary to inspect how keys are parsed
sed -n '38,200p' Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift

Length of output: 4903


Clarify Int Dictionary Test Comment
The test’s assertion correctly skips the "invalid" key (per decodeIntKeyedDictionary), but the inline comment “All Int keys are valid” is misleading. Please update it to reflect that non-integer keys are ignored.

• File: Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
• Line ~55: change

// All Int keys are valid

to something like

// Non-integer keys (e.g. "invalid") are ignored
🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
around line 55, the comment "// All Int keys are valid" is misleading because
the test actually ignores non-integer keys like "invalid". Update this comment
to clarify that non-integer keys are ignored, for example, change it to "//
Non-integer keys (e.g. \"invalid\") are ignored" to accurately describe the test
behavior.

Copy link
Contributor

@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: 11

♻️ Duplicate comments (20)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)

43-44: Consider improving the comment clarity

The comment could be more explicit about the design rationale.

Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (2)

37-37: Remove unused property or wire it into recoveredFrom method

The static property recoveredFromDebugOnlyError is never used in the codebase.


18-31: Document the Equatable behavior for error comparison

The Equatable implementation ignores error values and only compares the wasReported flag, which should be documented.

Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (1)

62-74: Consider differentiating between direct and fallback decoding outcomes

Both successful direct decoding and successful fallback decoding set the outcome to .decodedSuccessfully, which misses an opportunity to provide more granular debugging information.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)

38-47: Optimize superDecoder usage and improve error handling

Creating superDecoder twice is inefficient, and the error reporting could fail silently if the second decoder creation fails.

     // Try to decode the polymorphic value
+    let decoder = try superDecoder(forKey: key)
     do {
-      let decoder = try superDecoder(forKey: key)
       let value = try T.decode(from: decoder)
       return LossyOptionalPolymorphicValue(wrappedValue: value, outcome: .decodedSuccessfully)
     } catch {
       // Report error to resilient decoding error reporter
-      let decoder = try? superDecoder(forKey: key)
-      decoder?.reportError(error)
+      decoder.reportError(error)
       return LossyOptionalPolymorphicValue(wrappedValue: nil, outcome: .recoveredFrom(error, wasReported: true))
     }
Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (1)

46-60: Simplify nested error handling

Both catch blocks perform identical error reporting and rethrowing. The nested structure is unnecessary and creates code duplication. As noted in the previous review, this can be simplified to a single catch block.

Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)

30-41: Review assertion safety as noted in previous feedback

The extraction logic is correct, but the assert(!wasReported) at line 36 could cause debug crashes if the assumption is violated. The previous review correctly identified this potential safety issue.

Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (1)

47-57: Address production safety concern as noted previously

The debug-only projected value implementation is functionally correct, but the previous review correctly identified that the fatalError in non-DEBUG builds poses a production safety risk.

Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)

50-56: Simplify nested error handling structure

The inner try-catch block only reports and rethrows the error, which duplicates the outer catch behavior.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1)

6-6: Consider shortening the type name to comply with SwiftLint rules [LOW]

The type name LossyOptionalPolymorphicValueResilientTests is 46 characters, exceeding the 40-character limit.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)

82-85: Inconsistent error reporting flag

Individual element errors are reported via elementDecoder.reportError(error), but the aggregate error is marked as wasReported: false.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)

47-58: Consider memory implications for large arrays [MEDIUM]

The PolymorphicLossyArrayProjectedValue stores a results array with individual Result<T, Error> entries for each element. For large arrays, this could consume significant memory in DEBUG builds.

Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)

35-37: Replace assert with proper error handling [HIGH]

The assert(!wasReported) could crash the app in DEBUG builds. This concern was raised in a previous review but hasn't been addressed. Consider handling this case more gracefully or documenting why this condition should never occur.

Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1)

11-27: Consider protocol unification to reduce duplication [MEDIUM]

The ResilientProjectedValueProtocol duplicates functionality similar to PolymorphicProjectedValueProtocol. This was raised in a previous review. While module separation might justify this, consider if a shared base protocol could reduce duplication.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (1)

66-103: Refactor decoding logic to avoid multiple container accesses

The implementation accesses both singleValueContainer() and unkeyedContainer() from the same decoder, which may not be supported by all custom decoder implementations. This was raised in a previous review with a suggested solution.

Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (2)

65-65: Fix incorrect comment referencing Base64.

The comment incorrectly mentions Base64 when the test is about date parsing.

-        // Invalid Base64 format causes decoding failure
+        // Invalid date format causes decoding failure

89-89: Fix incorrect comment referencing Data instead of Date.

The comment incorrectly mentions Data when it should be Date.

-        // null values cannot be converted to Data
+        // null values cannot be converted to Date
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)

58-64: Inconsistent initialization between DEBUG and release builds.

The release build omits the outcome parameter when handling null values, creating an inconsistency.

    if try decodeNil(forKey: key) {
      #if DEBUG
      return PolymorphicLossyArrayValue(wrappedValue: [], outcome: .valueWasNil, results: [])
      #else
-     return PolymorphicLossyArrayValue(wrappedValue: [])
+     return PolymorphicLossyArrayValue(wrappedValue: [], outcome: .valueWasNil)
      #endif
    }
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (1)

6-6: Consider shortening the test suite name [LOW]

The type name exceeds SwiftLint's 40-character limit. Consider a shorter but still descriptive name.

Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (1)

210-286: Excellent implementation of configurable enum decoding [HIGH]

The RawRepresentable decoding provides great flexibility with the isFrozen flag, allowing teams to choose between strict validation and forward compatibility. The error helper method extraction improves readability as suggested in the previous review.

The comprehensive handling of all failure modes (missing key, nil, invalid raw value, type mismatch) with appropriate error reporting makes this very robust.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb4df4 and 38097d1.

📒 Files selected for processing (45)
  • README.md (1 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (6 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (3 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (4 hunks)
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (3 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (4 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2 hunks)
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (3 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift (5 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
Sources/KarrotCodableKit/**

⚙️ CodeRabbit Configuration File

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift
  • Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift
  • Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift
Tests/KarrotCodableKitTests/**

⚙️ CodeRabbit Configuration File

Tests/KarrotCodableKitTests/**: You are a senior Swift/iOS engineer reviewing runtime functionality tests for KarrotCodableKit's Codable extensions.

1. Runtime Behavior Testing [HIGH]

  • Verify JSON encoding/decoding scenarios cover real-world use cases
  • Check polymorphic type resolution testing across different strategies
  • Assess property wrapper testing (@DATEvalue, @DefaultFalse, etc.)
  • Validate error handling and edge case coverage

2. Test Data Quality [HIGH]

  • Review test JSON structures for realistic complexity
  • Check that TestDoubles provide comprehensive mock scenarios
  • Verify test data covers various data types and edge cases
  • Assess polymorphic test data represents actual usage patterns

3. Integration Testing [MEDIUM]

  • Check integration between macro-generated code and runtime functionality
  • Verify end-to-end scenarios combining multiple KarrotCodableKit features
  • Assess performance testing for large data structures
  • Review memory usage testing for complex polymorphic scenarios

Review Focus

  • Prioritize real-world usage scenario coverage
  • Focus on Codable correctness and performance implications
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift
*

⚙️ CodeRabbit Configuration File

*: You are a senior Swift/iOS engineer with 15+ years of experience reviewing KarrotCodableKit code changes.

Review Priorities [HIGH]

  • Code quality: memory leaks, error handling, readability (100 chars/line)
  • Architecture: SOLID principles, design patterns, DRY/KISS
  • Swift best practices: modern features, memory management, performance

Communication

  • Mark priority: [HIGH], [MEDIUM], [LOW]
  • Provide specific code examples
  • Focus on high-impact improvements over style

Files:

  • README.md
**/*.md

⚙️ CodeRabbit Configuration File

**/*.md: You are a senior technical writer reviewing documentation for KarrotCodableKit, a Swift Codable extension library.

1. Technical Accuracy [HIGH]

  • Verify code examples compile and work correctly
  • Check that API documentation matches actual implementation
  • Validate macro usage examples demonstrate proper syntax
  • Assess JSON examples are well-formed and realistic

2. Documentation Completeness [HIGH]

  • Review coverage of all major features (CustomCodable, PolymorphicCodable, AnyCodable, BetterCodable)
  • Check that complex concepts like polymorphic type resolution are well explained
  • Verify installation and setup instructions are current
  • Assess troubleshooting and error handling guidance

3. User Experience [MEDIUM]

  • Evaluate documentation structure and navigation clarity
  • Check that examples progress from simple to complex appropriately
  • Verify code snippets include necessary imports and context
  • Assess whether documentation answers common user questions

Review Focus

  • Prioritize accuracy and completeness over style
  • Focus on developer experience and practical usage scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • README.md
🧬 Code Graph Analysis (18)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (1)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (2)
Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (2)
  • testProjectedValueProvidesErrorInfo (34-69)
  • testErrorReporting (71-99)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (4)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (4)
  • decode (120-149)
  • decode (157-208)
  • decode (215-272)
  • lhs (99-101)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (1)
  • lhs (107-109)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (3)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Tests/KarrotCodableKitTests/PolymorphicCodable/PolymorphicLossyArrayValueTests.swift (1)
  • PolymorphicLossyArrayValueTests (13-112)
Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueTests.swift (1)
  • DataValueTests (12-47)
Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (6)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
  • decode (12-29)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (2)
  • lhs (134-136)
  • hash (140-142)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (2)
  • lhs (75-77)
  • hash (81-83)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2)
  • lhs (64-66)
  • hash (70-72)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (2)
Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (2)
  • lhs (92-94)
  • hash (98-100)
Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (2)
  • lhs (64-66)
  • hash (70-72)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessValue.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)
  • decodeIfPresent (48-69)
Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (3)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (38-38)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
  • arrayDecodingError (30-41)
Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)
  • dictionaryDecodingError (30-41)
Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)
  • flushReportedErrors (65-70)
  • errors (86-95)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (3)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (1)
  • decode (12-25)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • flushReportedErrors (65-70)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueTests.swift (1)
  • OptionalPolymorphicArrayValueTests (13-214)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (1)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (122-133)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (2)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicValueTests.swift (1)
  • OptionalPolymorphicValueTests (13-71)
🪛 SwiftLint (0.57.0)
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift

[Warning] 6-6: Type name 'LossyOptionalPolymorphicValueResilientTests' should be between 3 and 40 characters long

(type_name)

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift

[Warning] 6-6: Type name 'DefaultEmptyPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift

[Warning] 16-16: Type name 'ResilientOptionalPolymorphicArrayDummyResponse' should be between 3 and 40 characters long

(type_name)


[Warning] 30-30: Type name 'OptionalPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

🪛 GitHub Actions: CI
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift

[error] 140-140: Test 'error reporting with JSONDecoder' failed: Expected to decode String but found number instead for key 'notice'.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift

[error] 134-134: Test 'error reporting with JSONDecoder' failed: Expected to decode String but found a dictionary instead for key 'stringValue'.

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift

[error] 180-180: Test 'error reporting with JSONDecoder' failed: Expected to decode Bool but found a dictionary instead for key 'boolValue'; Expected to decode String but found an array instead for key 'stringValue'; Expected to decode Int but found a string instead for key 'intValue'.

Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift

[error] 119-119: Test 'error reporting with JSONDecoder' failed: Expected to decode String but found number instead for key 'base64Data'.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift

[error] 97-97: Test 'error reporting with JSONDecoder' failed: Expected to decode Double but found a string instead at index 1 and found null value instead at index 2 in 'doubleArray'; Expected to decode String but found null value instead at index 1 in 'stringArray'; Expected to decode Int but found a string instead at index 0 in 'intArray'.

Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift

[error] 126-126: Test 'error reporting with JSONDecoder' failed: Expected to decode String but found number instead for key 'isoDate'.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift

[error] 98-98: Test 'error reporting with JSONDecoder' failed: Expected to decode Int but found a string instead at index 1 in 'integers' array.


[error] 127-127: Test 'decode with reportResilientDecodingErrors' failed: Expected to decode Int but found a string instead at index 1 in 'integers' array.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift

[error] 140-140: Test 'error reporting with JSONDecoder' failed: Expected to decode String but found number instead for key 'notice'.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift

[error] 113-113: Test 'error reporting with JSONDecoder' failed: Expected to decode Int but found bool instead for key 'c' and expected Int but found string instead for key 'a' in 'stringDict'.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift

[error] 140-140: Test 'error reporting with JSONDecoder' failed: Expected to decode String but found number instead for key 'notice'.

🔇 Additional comments (65)
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueTests.swift (1)

164-164: LGTM - Comment standardization improvement

Good catch on the typo fix. This improves consistency with standard testing comment conventions.

[LOW]

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swift (1)

120-120: LGTM - Consistent comment standardization

Matches the comment improvement pattern across test files. Good attention to detail.

[LOW]

README.md (1)

280-280: Good attribution for adapted functionality

Proper acknowledgment of the source for ResilientDecodingOutcome aligns well with the PR's introduction of resilient decoding features. This helps developers understand the provenance of the functionality.

[MEDIUM]

Tests/KarrotCodableKitTests/PolymorphicCodable/UnnestedPolymorphicTests/UnnestedPolymorphicDecodableTests.swift (1)

59-59: Excellent safety improvement using #require

Great enhancement replacing forced unwrapping with Swift Testing's #require for data conversion. This prevents potential runtime crashes and provides better error diagnostics when string-to-data conversion fails. The explicit UTF-8 encoding is also more descriptive than relying on the default.

[HIGH]

Also applies to: 85-85, 109-109, 128-128, 148-148

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueTests.swift (1)

116-116: LGTM - Test comment formatting standardization

Clean formatting improvement removing colons from test section comments. This creates consistency across the test suite and improves readability.

[LOW]

Also applies to: 133-133, 136-136, 163-163

Sources/KarrotCodableKit/BetterCodable/DateValue/DateValue.swift (1)

27-42: Well-structured resilient decoding implementation

The addition of outcome tracking and debug-only projected value follows the established pattern across the codebase. The error reporting integration is properly implemented.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)

16-48: Consistent and robust decoding implementation

The explicit checks for key existence and nil values provide clear outcome tracking. The pattern is consistent with other polymorphic array decoders in the codebase.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicValue.swift (1)

27-73: Excellent resilient decoding implementation

The addition of outcome tracking, error reporting, and debug diagnostics follows the established pattern. The explicit Equatable and Hashable conformances improve type safety.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1)

101-142: Test validates error reporting correctly

The test properly verifies that type mismatches are caught and reported. The pipeline failure message appears to have an incorrect line number (140 instead of 124), but the test logic is sound.

Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift (3)

27-42: LGTM! Resilient decoding infrastructure properly integrated

The outcome tracking and projected value implementation follows the established pattern across the framework. The debug-only availability of projectedValue is a good security practice.


69-73: LGTM! Proper Equatable implementation

The equality comparison correctly focuses on the wrapped value only, which is the appropriate semantic for this property wrapper.


75-79: LGTM! Consistent Hashable implementation

The hashing implementation correctly matches the Equatable behavior by only considering the wrapped value.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicArrayValue.swift (4)

22-47: LGTM! Consistent resilient decoding implementation

The outcome tracking and projected value infrastructure is properly implemented and follows the established pattern. The use of PolymorphicProjectedValue is appropriate for polymorphic types.


60-61: LGTM! Clean outcome tracking integration

The addition of outcome tracking is minimal and doesn't disrupt the existing decoding logic.


74-78: LGTM! Proper Equatable implementation

The equality comparison correctly focuses on the wrapped array values, maintaining consistency with other property wrappers in the framework.


80-84: LGTM! Consistent Hashable implementation

The hashing implementation properly matches the Equatable behavior and maintains the hash/equality contract.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (5)

11-30: LGTM! Comprehensive nil value test

The test properly validates both the decoding behavior and the resilient outcome tracking for null values. The debug-only assertions are appropriately guarded.


32-49: LGTM! Important edge case coverage

The missing key test properly validates the expected behavior and outcome tracking when keys are absent from the JSON.


51-80: LGTM! Comprehensive success case test

The test thoroughly validates successful polymorphic decoding with realistic JSON data and proper outcome tracking. The property validation is comprehensive.


82-99: LGTM! Proper error handling validation

The test correctly verifies that unknown polymorphic types trigger decoding errors rather than being silently ignored.


101-115: Test isolation appears valid; pipeline failure likely from another test

After searching, there are no occurrences of "notice" with a numeric value in OptionalPolymorphicValueResilientTests.swift. The only other test touching notice is in
Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift, which supplies a nested object for notice.

• No "notice": <number> patterns in this file
• The error reporting with JSONDecoder test in Value/PolymorphicValueResilientTests.swift may be the culprit
• Please inspect the full pipeline logs to identify which test is actually failing

The “invalidJSONThrowsError” test itself is correctly structured and can be approved; the reported mismatch (Expected to decode String but found number) seems to originate elsewhere.

Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)

10-28: LGTM! Well-designed error aggregation infrastructure [HIGH]

The DictionaryDecodingError struct provides clean, type-safe error aggregation for dictionary decoding. The generic design and use of Result type are excellent Swift patterns.

Sources/KarrotCodableKit/PolymorphicCodable/DefaultEmptyPolymorphicArrayValue.swift (3)

62-79: Excellent error handling enhancement [HIGH]

The replacement of print statements with proper decoder.reportError(error) calls significantly improves the debugging experience. The outcome tracking provides valuable diagnostic information while maintaining the fallback behavior.


91-95: LGTM! Proper Equatable implementation

The equality comparison correctly focuses on the wrapped array values. This is semantically appropriate and consistent with the framework's approach.


97-101: LGTM! Consistent Hashable implementation

The hashing implementation properly matches the Equatable behavior and maintains the essential hash/equality contract.

Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)

28-43: Well-structured resilient decoding implementation

The addition of outcome tracking and projected value follows the established pattern correctly. The debug-only projected value is a good design choice for production performance.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueResilientTests.swift (2)

11-163: Comprehensive test coverage for resilient decoding

The test suite thoroughly covers all decoding scenarios including nil values, missing keys, successful decoding, unknown types, and error reporting. The debug-only assertions are properly guarded.


136-162: Swift tests unavailable in sandbox—please verify locally

The sandbox environment doesn’t include the swift toolchain, so I couldn’t reproduce the failure. Please run the specific test in your local setup to confirm whether it passes or still triggers the pipeline error.

• Run locally:

swift test --filter "LossyOptionalPolymorphicValueResilientTests/errorReporterCalled"

• Verify that the error reporter behaves as expected when decoding an unknown polymorphic type.
• Confirm that the pipeline error (“Expected to decode String but found number…”) aligns with your test data or identify any mismatch in test configuration.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicProjectedValue.swift (1)

11-59: Clean projected value abstraction

The protocol-based design with default implementation provides a good abstraction for polymorphic projected values. The separation between simple and array-specific projected values is well thought out.

Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (1)

65-74: Well-implemented error handling pattern [LOW]

The decoding implementation correctly distinguishes this wrapper from lossy variants by rethrowing errors instead of recovering. This maintains the expected behavior where decoding failures propagate up.

Sources/KarrotCodableKit/BetterCodable/ResilientProjectedValue.swift (1)

45-77: Elegant use of dynamic member lookup [LOW]

The dynamic member lookup implementation for array and dictionary projected values provides excellent developer ergonomics. This allows direct access to error details without explicit method calls.

Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (1)

68-80: Proper implementation of lossy decoding pattern [LOW]

The error handling correctly implements the lossy behavior by catching errors, reporting them via decoder.reportError, and recovering with nil. The outcome tracking with wasReported: true ensures proper error aggregation.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (3)

12-33: Well-structured test fixture for comprehensive coverage.

Good test design with various data types and a non-convertible NestedObject to properly test the lossy behavior of @LosslessArray.


34-69: Excellent test coverage for projected value functionality.

The test comprehensively validates:

  • Lossy array filtering behavior for various types
  • Correct conversion of compatible values
  • Error tracking through projected values in DEBUG builds

101-143: Good coverage of failure scenarios.

Tests properly validate that:

  • Non-array values cause complete decoding failure
  • Missing required keys result in decoding errors

The async confirmation pattern is well-used for error assertions.

Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2)

12-25: Clean implementation of optional array decoding.

The method properly delegates to decodeIfPresent and correctly sets the .keyNotFound outcome when the value is not present.


27-68: Robust implementation with comprehensive error handling.

The method excellently handles all edge cases:

  • Missing keys return nil
  • Null values are explicitly handled with .valueWasNil outcome
  • Array elements are properly decoded using PolymorphicValue
  • Errors are reported before rethrowing for debugging
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift (4)

32-134: Comprehensive test coverage for successful decoding scenarios.

Excellent test design covering:

  • Valid polymorphic array decoding with type verification
  • Empty arrays, null values, and missing keys
  • Proper DEBUG-only projected value assertions

136-188: Well-designed error reporting tests.

Tests properly validate:

  • Decoding failures for invalid array elements
  • Type mismatch errors for non-array values
  • Error collection through the resilient decoding error reporter

192-247: Thorough projected value testing for DEBUG builds.

Tests correctly verify outcome states:

  • .decodedSuccessfully for valid arrays
  • .valueWasNil for explicit nulls
  • .keyNotFound for missing keys

249-287: Excellent integration test with multiple properties.

The test effectively validates mixed decoding scenarios across multiple properties, ensuring each maintains its correct state and outcome.

Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (2)

20-45: Well-structured date value resilient decoding tests.

The test properly validates:

  • Multiple date decoding strategies
  • Successful decoding with valid formats
  • Projected value outcomes in DEBUG builds

Note: The Korean comments mentioned in previous reviews appear to have been translated.


95-128: Verify resilient error‐handling in DateValue decoders

It looks like type mismatches in your DateValue initializers are throwing immediately, so the error reporter never sees them. I wasn’t able to find an init(from:) override in the BetterCodable/DateValue folder that wraps each decode call in the resilient error-capturing helper.

Please double-check:

  • In Sources/KarrotCodableKit/BetterCodable/DateValue/… (ISO, RFC, Timestamp variants), each init(from:) uses the resilient decoding helper (e.g. resilientTry, decodeIfPresent(..., default:), etc.)
  • If any branch is missing that wrapper, update it so type mismatches get reported instead of thrown
  • Otherwise, adjust the test to expect an immediate DecodingError rather than collected errors
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)

12-46: Robust lossy array decoding implementation.

The method properly handles all edge cases with appropriate fallback to empty arrays:

  • Missing keys → empty array with .keyNotFound
  • Null values → empty array with .valueWasNil
  • Decoding failures → empty array with .recoveredFrom error
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (2)

11-30: Well-structured test for empty array decoding

The test properly verifies the expected behavior when decoding an empty array, including the resilient decoding outcome in DEBUG builds.


32-203: Comprehensive test coverage for resilient decoding scenarios [HIGH]

Excellent test suite that covers all edge cases for polymorphic array decoding:

  • Empty arrays, successful decoding, invalid elements
  • Missing keys, null values, invalid types
  • Error reporting integration

The tests properly verify both the decoded values and the resilient decoding outcomes in DEBUG builds.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (2)

21-49: Excellent test coverage for successful type conversions [HIGH]

The test properly validates that LosslessValue correctly converts between different primitive types and tracks successful decoding outcomes.


101-136: Test implementation looks correct despite pipeline failure [MEDIUM]

The error reporting test properly validates that complex types cannot be decoded and that errors are reported through the resilient decoding mechanism. The pipeline failure appears to be environmental or related to test execution rather than a logic error.

The test correctly:

  • Attempts to decode incompatible types
  • Expects the decoding to throw
  • Verifies error reporting in DEBUG builds
Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1)

1-217: Thorough test coverage for lossy polymorphic array decoding [HIGH]

Excellent test suite that validates the lossy array behavior where invalid elements are skipped rather than failing the entire decoding. The tests properly verify:

  • Element-by-element decoding results
  • Partial success scenarios
  • Error reporting integration

The pattern matching for result verification (lines 117-134) is functional, though could be more concise as noted in previous reviews.

Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift (1)

39-143: Well-implemented resilient decoding support [HIGH]

The implementation properly adds resilient decoding capabilities to PolymorphicLossyArrayValue:

  • Clean separation of DEBUG/non-DEBUG behavior
  • Appropriate use of projectedValue for error access
  • Proper protocol conformances

The lossy behavior correctly skips invalid elements while maintaining decoding position.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1)

1-164: Comprehensive test suite for lossy dictionary decoding [HIGH]

Excellent test coverage that validates:

  • Partial decoding success with invalid entries
  • Per-key error tracking through projected values
  • Error reporting integration
  • Edge cases (complete failure, missing keys)

The test properly verifies that invalid key-value pairs are skipped while valid ones are retained, which is the expected lossy behavior.

Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (6)

18-35: LGTM! Well-structured resilient decoding support

The addition of outcome tracking and debug-only projectedValue follows the consistent pattern across other property wrappers in the codebase. The dual initializers properly handle both public API and internal construction with outcome.


64-74: Clean helper types for decoding state management

The ExtractedKey and DecodingState structs are well-designed. Good use of conditional compilation to track detailed results only in debug builds, avoiding overhead in release builds.


135-183: Excellent refactoring to eliminate force casts [HIGH]

The safe casting pattern properly handles type mismatches by skipping invalid keys rather than crashing. This addresses the previous force cast concerns while maintaining the intended behavior of filtering out invalid key-value pairs.

The error reporting integration is also well done - capturing errors and reporting them through the decoder while continuing to process valid entries.


185-196: Well-implemented outcome determination logic

The method correctly identifies partial decoding failures by comparing element and result counts, creating appropriate outcomes with error details in debug builds.


198-234: Clean and well-organized decoding implementation

The refactored decoding logic with early nil detection and type-specific delegation improves readability significantly. Proper error reporting and outcome tracking throughout all code paths.


268-280: Consistent implementation with other property wrappers

The KeyedDecodingContainer extension follows the established pattern, properly handling missing keys by returning empty dictionaries with appropriate outcomes.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (3)

39-58: Well-designed API for error reporting control

The temporary error reporter installation pattern in the decode method is excellent - it ensures error collection is scoped to a single decode operation and properly cleans up afterwards.


99-114: Elegant hierarchical error storage implementation

The recursive Node structure with path-based insertion is a clean solution for organizing errors by their coding paths. Good use of Swift's collection APIs.


121-134: Robust error reporting integration

Good defensive programming with proper unwrapping and type checking. The assertion failure appropriately flags programming errors during development.

Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (3)

19-29: Clear API design for controlling unknown value behavior [HIGH]

The isFrozen property provides a clean way to control whether unknown raw values should be treated as errors or silently use defaults. The default implementation ensures backward compatibility.


66-88: Comprehensive error handling with proper fallback behavior

The decoding implementation correctly prioritizes nil detection, handles errors gracefully with reporting, and provides detailed outcomes for debugging. Good defensive programming.


157-208: Practical bool conversion with comprehensive error tracking

The enhanced bool decoding handles common API patterns where booleans are sent as integers or strings. Good separation between type mismatch errors (which attempt conversion) and other errors (which don't).

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1)

1-161: Comprehensive test coverage for polymorphic array resilient decoding

Excellent test suite covering all major scenarios including edge cases. Good use of conditional compilation to test debug-only features. The tests properly validate both the functional behavior (values decoded correctly) and the diagnostic features (outcomes and error reporting).

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1)

1-423: Excellent comprehensive test coverage for DefaultCodable resilient features [HIGH]

This test suite thoroughly validates all aspects of the resilient decoding implementation:

  • Error capture and projected value access
  • Default value fallback for various failure modes
  • RawRepresentable handling with the new isFrozen flag
  • Error reporter integration

The Korean comments have been properly translated to English as requested in previous reviews.

Regarding the pipeline failure at line 180 - this appears to be a false positive. The test is designed to handle these decoding errors gracefully by falling back to default values, which is the expected behavior.

Comment on lines +67 to +72
if elements.count == results.count {
self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
} else {
let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Logic issue in outcome determination

The condition elements.count == results.count will always be true because both arrays are incremented in the same loop iterations. This means the outcome will always be .decodedSuccessfully even when errors occurred.

Consider checking if any errors were recorded instead:

-    if elements.count == results.count {
+    if results.allSatisfy({ if case .success = $0 { true } else { false } }) {
       self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
     } else {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
around lines 67 to 72, the condition comparing elements.count and results.count
is flawed because both arrays grow simultaneously, making the condition always
true and incorrectly marking the outcome as .decodedSuccessfully. Instead,
modify the logic to check if any decoding errors were recorded during processing
and set the outcome to .recoveredFrom with the error if any exist; otherwise,
use .decodedSuccessfully.

Comment on lines 36 to 98
extension LossyArray: Decodable where T: Decodable {
private struct AnyDecodableValue: Decodable {}

public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()

var elements: [T] = []
while !container.isAtEnd {
do {
let value = try container.decode(T.self)
elements.append(value)
} catch {
_ = try? container.decode(AnyDecodableValue.self)
do {
// Check for null first
let singleValueContainer = try decoder.singleValueContainer()
if singleValueContainer.decodeNil() {
#if DEBUG
self.init(wrappedValue: [], outcome: .valueWasNil)
#else
self.init(wrappedValue: [])
#endif
return
}
} catch {
// Not a single value container, proceed with array decoding
}

do {
var container = try decoder.unkeyedContainer()

var elements: [T] = []
#if DEBUG
var results: [Result<T, Error>] = []
#endif

while !container.isAtEnd {
let elementDecoder = try container.superDecoder()
do {
let value = try elementDecoder.singleValueContainer().decode(T.self)
elements.append(value)
#if DEBUG
results.append(.success(value))
#endif
} catch {
elementDecoder.reportError(error)
#if DEBUG
results.append(.failure(error))
#endif
}
}

self.wrappedValue = elements
#if DEBUG
if elements.count == results.count {
self.init(wrappedValue: elements, outcome: .decodedSuccessfully)
} else {
let error = ResilientDecodingOutcome.ArrayDecodingError(results: results)
self.init(wrappedValue: elements, outcome: .recoveredFrom(error, wasReported: false))
}
#else
self.init(wrappedValue: elements)
#endif
} catch {
decoder.reportError(error)
#if DEBUG
self.init(wrappedValue: [], outcome: .recoveredFrom(error, wasReported: true))
#else
self.init(wrappedValue: [])
#endif
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Well-implemented resilient array decoding with one issue

The implementation correctly handles nil values, element-wise decoding with error tracking, and provides appropriate debug information. However, the error reporting flag inconsistency mentioned above should be addressed.

🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift between
lines 36 and 98, the error reporting flag passed to the outcome in the debug
initializer is inconsistent: when creating the outcome with
.recoveredFrom(error, wasReported: false) inside the array decoding block, it
should reflect whether the error was actually reported. Fix this by setting the
wasReported flag to true when the error is reported to ensure consistent and
accurate error reporting state.

Comment on lines +46 to +49
#if DEBUG
/// Results of decoding each element in the array (DEBUG only)
let results: [Result<PolymorphicType.ExpectedType, Error>]
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider limiting tracked results for large arrays [MEDIUM]

In DEBUG mode, storing results for every array element could cause memory issues with large arrays. Consider implementing a reasonable limit:

#if DEBUG
private static let maxTrackedResults = 1000

// In init(from decoder:)
while !container.isAtEnd {
  do {
    let value = try container.decode(PolymorphicValue<PolymorphicType>.self).wrappedValue
    elements.append(value)
    #if DEBUG
    if results.count < Self.maxTrackedResults {
      results.append(.success(value))
    }
    #endif
  } catch {
    _ = try? container.decode(AnyDecodableValue.self)
    #if DEBUG
    if results.count < Self.maxTrackedResults {
      results.append(.failure(error))
    }
    #endif
  }
}
#endif

Also applies to: 97-120

🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/PolymorphicCodable/PolymorphicLossyArrayValue.swift
around lines 46 to 49 and also lines 97 to 120, the DEBUG mode stores decoding
results for every array element, which can cause memory issues with large
arrays. To fix this, define a private static constant maxTrackedResults (e.g.,
1000) and modify the code in the init(from decoder:) method to append results
only if the current results count is less than maxTrackedResults, both for
success and failure cases, thereby limiting the number of tracked results.

Comment on lines +12 to +23
public struct ArrayDecodingError<Element>: Error {
public let results: [Result<Element, Error>]
public var errors: [Error] {
results.compactMap { result in
switch result {
case .success:
return nil
case .failure(let error):
return error
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider memory optimization for large arrays [MEDIUM]

The ArrayDecodingError struct stores all decoding results, including successes. For large arrays, this could consume significant memory even in DEBUG builds. Consider storing only failures or limiting the number of stored results.

 public struct ArrayDecodingError<Element>: Error {
-  public let results: [Result<Element, Error>]
+  public let failures: [(index: Int, error: Error)]
   public var errors: [Error] {
-    results.compactMap { result in
-      switch result {
-      case .success:
-        return nil
-      case .failure(let error):
-        return error
-      }
-    }
+    failures.map { $0.error }
   }
   
-  public init(results: [Result<Element, Error>]) {
-    self.results = results
+  public init(results: [Result<Element, Error>]) {
+    self.failures = results.enumerated().compactMap { index, result in
+      if case .failure(let error) = result {
+        return (index, error)
+      }
+      return nil
+    }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public struct ArrayDecodingError<Element>: Error {
public let results: [Result<Element, Error>]
public var errors: [Error] {
results.compactMap { result in
switch result {
case .success:
return nil
case .failure(let error):
return error
}
}
}
public struct ArrayDecodingError<Element>: Error {
public let failures: [(index: Int, error: Error)]
public var errors: [Error] {
failures.map { $0.error }
}
public init(results: [Result<Element, Error>]) {
self.failures = results.enumerated().compactMap { index, result in
if case .failure(let error) = result {
return (index, error)
}
return nil
}
}
}
🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift around lines 12
to 23, the struct currently stores all decoding results including successes,
which can use excessive memory for large arrays. Modify the implementation to
store only the failures or limit the number of stored results to reduce memory
usage, especially in DEBUG builds. Adjust the stored property and related logic
accordingly to keep only necessary error information.

Comment on lines 25 to 45
fileprivate mutating func replaceResilientDecodingErrorReporter(
with errorReporter: ResilientDecodingErrorReporter
) -> Any? {
if let existingValue = self[.resilientDecodingErrorReporter] {
assertionFailure()
if let existingReporter = existingValue as? ResilientDecodingErrorReporter {
existingReporter.currentDigest.mayBeMissingReportedErrors = true
}
}
self[.resilientDecodingErrorReporter] = errorReporter
return errorReporter
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider thread safety for error reporter replacement [MEDIUM]

The replaceResilientDecodingErrorReporter method modifies the existing reporter's state with mayBeMissingReportedErrors = true but doesn't consider concurrent access. If the decoder is used across threads, this could lead to race conditions.

Consider either:

  1. Documenting that decoders with error reporters should not be shared across threads
  2. Adding synchronization to protect the reporter state
🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/Resilient/ErrorReporting.swift around lines 25 to
36, the method replaceResilientDecodingErrorReporter modifies the error
reporter's state without thread safety, risking race conditions if accessed
concurrently. To fix this, either add synchronization mechanisms (like a serial
dispatch queue or a lock) around the state modification and replacement to
ensure thread-safe access, or clearly document that decoders using error
reporters must not be shared across threads to prevent concurrent modifications.

Comment on lines 13 to 148
struct LossyArrayResilientTests {
struct Fixture: Decodable {
@LossyArray var integers: [Int]
@LossyArray var strings: [String]

struct NestedObject: Decodable, Equatable {
let id: Int
let name: String
}
@LossyArray var objects: [NestedObject]
}

@Test("projected value provides error information in DEBUG")
func testProjectedValueProvidesErrorInfo() throws {
let json = """
{
"integers": [1, "invalid", 3, null, 5],
"strings": ["hello", 123, "world", null],
"objects": [
{"id": 1, "name": "first"},
{"id": "invalid", "name": "second"},
{"id": 3, "name": "third"}
]
}
"""

let decoder = JSONDecoder()
let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(Fixture.self, from: data)

// Verify default behavior
#expect(fixture.integers == [1, 3, 5])
#expect(fixture.strings == ["hello", "world"])
#expect(fixture.objects == [
Fixture.NestedObject(id: 1, name: "first"),
Fixture.NestedObject(id: 3, name: "third")
])

#if DEBUG
// Access error info through projected value
#expect(fixture.$integers.results.count == 5)
#expect(fixture.$integers.errors.count == 2) // "invalid" and null

// Check success/failure of each element
let intResults = fixture.$integers.results
#expect(try intResults[0].get() == 1)
#expect(intResults[1].isFailure == true)
#expect(try intResults[2].get() == 3)
#expect(intResults[3].isFailure == true)
#expect(try intResults[4].get() == 5)

// strings validation
#expect(fixture.$strings.results.count == 4)
#expect(fixture.$strings.errors.count == 2) // 123 and null

// objects validation
#expect(fixture.$objects.results.count == 3)
#expect(fixture.$objects.errors.count == 1) // "invalid" id
#endif
}

@Test("error reporting with JSONDecoder")
func testErrorReporting() throws {
let json = """
{
"integers": [1, "two", 3],
"strings": ["a", "b", "c"],
"objects": []
}
"""

let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()

let data = try #require(json.data(using: .utf8))
_ = try decoder.decode(Fixture.self, from: data)

let errorDigest = errorReporter.flushReportedErrors()

#if DEBUG
// Check if errors were reported
let digest = try #require(errorDigest)
#expect(digest.errors.count >= 1)
#else
// No error info in Release builds
#expect(errorDigest == nil)
#endif
}

@Test("decode with reportResilientDecodingErrors")
func testDecodeWithReportFlag() throws {
let json = """
{
"integers": [1, "invalid", 3],
"strings": [],
"objects": []
}
"""

let decoder = JSONDecoder()
let data = try #require(json.data(using: .utf8))

let (fixture, errorDigest) = try decoder.decode(
Fixture.self,
from: data,
reportResilientDecodingErrors: true
)

#expect(fixture.integers == [1, 3])

#if DEBUG
#expect(errorDigest != nil)
#expect(errorDigest?.errors.count ?? 0 >= 1)
#else
#expect(errorDigest == nil)
#endif
}

@Test("empty array on complete failure")
func testEmptyArrayOnCompleteFailure() throws {
let json = """
{
"integers": "not an array",
"strings": 123,
"objects": null
}
"""

let decoder = JSONDecoder()
let data = try #require(json.data(using: .utf8))

let fixture = try decoder.decode(Fixture.self, from: data)

#expect(fixture.integers == [])
#expect(fixture.strings == [])
#expect(fixture.objects == [])

#if DEBUG
// Error info when entire array decoding fails
#expect(fixture.$integers.error != nil)
#expect(fixture.$strings.error != nil)
#expect(fixture.$objects.error == nil) // null is not an error
#endif
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Well-structured tests with execution issues

The test suite provides comprehensive coverage of LossyArray resilient decoding scenarios. However, the pipeline failures suggest there might be an issue with how errors are handled during array element decoding. The tests themselves appear correctly written.

🧰 Tools
🪛 GitHub Actions: CI

[error] 98-98: Test 'error reporting with JSONDecoder' failed: Expected to decode Int but found a string instead at index 1 in 'integers' array.


[error] 127-127: Test 'decode with reportResilientDecodingErrors' failed: Expected to decode Int but found a string instead at index 1 in 'integers' array.

🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
between lines 13 and 157, the test suite is well-structured but likely fails due
to improper error handling during array element decoding in the LossyArray
property wrapper. Review the LossyArray implementation to ensure it correctly
captures and manages decoding errors for individual elements without throwing,
allowing the tests to pass. Fix the decoding logic to catch and store errors per
element, returning only successfully decoded items, and provide error info via
the projected value as expected by the tests.

Comment on lines 74 to 95
@Test("error reporting with JSONDecoder")
func testErrorReporting() throws {
let json = """
{
"integers": [1, "two", 3],
"strings": ["a", "b", "c"],
"objects": []
}
"""

let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()

let data = try #require(json.data(using: .utf8))
_ = try decoder.decode(Fixture.self, from: data)

let errorDigest = errorReporter.flushReportedErrors()

#if DEBUG
// Check if errors were reported
let digest = try #require(errorDigest)
#expect(digest.errors.count >= 1)
#else
// No error info in Release builds
#expect(errorDigest == nil)
#endif
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Test failure suggests error handling issue

The test expects errors to be collected and reported, but the pipeline failure indicates an error is being thrown instead. This suggests LossyArray might not be properly catching and collecting element decoding errors.

Run the following script to verify error handling:


🏁 Script executed:

#!/bin/bash
# Check if LossyArray properly catches element decoding errors
ast-grep --pattern 'do {
  let value = try elementDecoder.singleValueContainer().decode(T.self)
  $$$
} catch {
  elementDecoder.reportError(error)
  $$$
}'

Length of output: 158


I wasn’t able to find any catch/report‐error block in the LossyArray decoder implementation, nor the extensions that wire in the enableResilientDecodingErrorReporting() hook. We need to inspect those to confirm if errors are ever caught and forwarded to the reporter instead of being thrown. Let’s locate the core pieces:

Once we confirm if (and how) errors are caught and reported in LossyArray’s init(from:), we can determine whether the missing catch/report is the root cause of the test failure.


🏁 Script executed:

#!/bin/bash
# 1) Find where we wire up the error reporter
rg -n "enableResilientDecodingErrorReporting" -C5

# 2) Find the reporter type
rg -n "ResilientDecodingErrorReporter" -C5

# 3) Find the LossyArray implementation
rg -n "struct LossyArray" -C5
rg -n "extension LossyArray" -C5

Length of output: 34829


Let’s inspect the full init(from:) body to verify whether we ever call reportError(_:) inside the per-element catch:


🏁 Script executed:

#!/bin/bash
# Dump LossyArray.swift to review init(from:) and all catch blocks
sed -n '1,200p' Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift

Length of output: 3248


Inspecting how reportError(_:) is declared will confirm whether it’s available on the Decoder returned by container.superDecoder(). Let’s dump the relevant lines around its definition:


🏁 Script executed:

#!/bin/bash
# Inspect reportError(_:)'s extension receiver
sed -n '100,140p' Sources/KarrotCodableKit/Resilient/ErrorReporting.swift

Length of output: 1111


Let’s verify how ResilientDecodingErrorReporter tracks errors (i.e. resilientDecodingHandled and hasErrors) to ensure that flushReportedErrors() actually returns non-nil when an element fails:

This will confirm whether an element-level reportError(_:) sets hasErrors = true.


🏁 Script executed:

#!/bin/bash
# Dump the reporter class for error-tracking logic
rg -n "public final class ResilientDecodingErrorReporter" -C20 Sources/KarrotCodableKit/Resilient/ErrorReporting.swift

Length of output: 1390


Guard error reporting behind DEBUG to satisfy Release‐build tests

Right now LossyArray’s per‐element and outer‐catch calls to reportError(_:) run in all builds, so even in Release you’ll collect errors and flushReportedErrors() returns a non‐nil digest—breaking the test that expects nil. Wrap those reportError invocations in #if DEBUG so only Debug builds report errors.

Locations to update in Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift (inside init(from:)):

  • Element‐decoding catch (around line 55)
  • Outer‐catch (around line 76)

Proposed diff:

@@ while !container.isAtEnd {
     do {
         let value = try elementDecoder
             .singleValueContainer()
             .decode(T.self)
         elements.append(value)
         #if DEBUG
         results.append(.success(value))
         #endif
     } catch {
-        elementDecoder.reportError(error)
         #if DEBUG
+        elementDecoder.reportError(error)
         results.append(.failure(error))
         #endif
     }
 }

@@ } catch {
-    decoder.reportError(error)
     #if DEBUG
+    decoder.reportError(error)
     self.init(
         wrappedValue: [],
         outcome: .recoveredFrom(error, wasReported: true)
     )
     #else
     self.init(wrappedValue: [])
     #endif
 }

With these guards, Release builds won’t call reportError(_:) and flushReportedErrors() will return nil as the test expects.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test("error reporting with JSONDecoder")
func testErrorReporting() throws {
let json = """
{
"integers": [1, "two", 3],
"strings": ["a", "b", "c"],
"objects": []
}
"""
let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()
let data = try #require(json.data(using: .utf8))
_ = try decoder.decode(Fixture.self, from: data)
let errorDigest = errorReporter.flushReportedErrors()
#if DEBUG
// Check if errors were reported
let digest = try #require(errorDigest)
#expect(digest.errors.count >= 1)
#else
// No error info in Release builds
#expect(errorDigest == nil)
#endif
}
// In Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift
// within the `init(from:)` implementation, update the two error‐reporting sites:
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var elements: [T] = []
var results: [Result<T, Error>] = []
while !container.isAtEnd {
let elementDecoder = try container.nestedContainer(keyedBy: CodingKeys.self)
do {
let value = try elementDecoder
.singleValueContainer()
.decode(T.self)
elements.append(value)
#if DEBUG
results.append(.success(value))
#endif
} catch {
#if DEBUG
elementDecoder.reportError(error)
results.append(.failure(error))
#endif
}
}
do {
self.init(
wrappedValue: elements,
outcome: .recoveredFromMultiple(results, decoder: decoder)
)
} catch {
#if DEBUG
decoder.reportError(error)
self.init(
wrappedValue: [],
outcome: .recoveredFrom(error, wasReported: true)
)
#else
self.init(wrappedValue: [])
#endif
}
}
🧰 Tools
🪛 GitHub Actions: CI

[error] 98-98: Test 'error reporting with JSONDecoder' failed: Expected to decode Int but found a string instead at index 1 in 'integers' array.

🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyArray.swift around
lines 55 and 76, the calls to reportError(_:) inside the element-decoding catch
and the outer catch blocks are currently executed in all builds, causing error
reporting even in Release builds. To fix this, wrap these reportError(_:) calls
in #if DEBUG ... #endif preprocessor directives so that error reporting only
happens in Debug builds. This will ensure that in Release builds, no errors are
reported and flushReportedErrors() returns nil as expected by the tests.

Comment on lines +15 to +28
@CustomCodable(codingKeyStyle: .snakeCase)
struct ResilientOptionalPolymorphicArrayDummyResponse {
@DummyNotice.OptionalPolymorphicArray
var notices: [any DummyNotice]?

@DummyNotice.OptionalPolymorphicArray
var notices2: [any DummyNotice]?

@DummyNotice.OptionalPolymorphicArray
var notices3: [any DummyNotice]?

@DummyNotice.OptionalPolymorphicArray
var notices4: [any DummyNotice]?
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider shortening the type name for better readability.

The type name ResilientOptionalPolymorphicArrayDummyResponse exceeds SwiftLint's recommended length. Consider a more concise name while maintaining clarity.

-struct ResilientOptionalPolymorphicArrayDummyResponse {
+struct OptionalArrayResilientResponse {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@CustomCodable(codingKeyStyle: .snakeCase)
struct ResilientOptionalPolymorphicArrayDummyResponse {
@DummyNotice.OptionalPolymorphicArray
var notices: [any DummyNotice]?
@DummyNotice.OptionalPolymorphicArray
var notices2: [any DummyNotice]?
@DummyNotice.OptionalPolymorphicArray
var notices3: [any DummyNotice]?
@DummyNotice.OptionalPolymorphicArray
var notices4: [any DummyNotice]?
}
@CustomCodable(codingKeyStyle: .snakeCase)
struct OptionalArrayResilientResponse {
@DummyNotice.OptionalPolymorphicArray
var notices: [any DummyNotice]?
@DummyNotice.OptionalPolymorphicArray
var notices2: [any DummyNotice]?
@DummyNotice.OptionalPolymorphicArray
var notices3: [any DummyNotice]?
@DummyNotice.OptionalPolymorphicArray
var notices4: [any DummyNotice]?
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 16-16: Type name 'ResilientOptionalPolymorphicArrayDummyResponse' should be between 3 and 40 characters long

(type_name)

🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift
between lines 15 and 28, the struct name
ResilientOptionalPolymorphicArrayDummyResponse is too long and reduces
readability. Rename this struct to a shorter but still descriptive name, such as
ResilientPolymorphicArrayResponse or ResilientOptionalArrayResponse, to comply
with SwiftLint's length recommendations while keeping the purpose clear.

var notices4: [any DummyNotice]?
}

struct OptionalPolymorphicArrayValueResilientTests {
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider shortening the test suite name.

Similar to the type name, the suite name exceeds SwiftLint's recommendation.

-struct OptionalPolymorphicArrayValueResilientTests {
+struct OptionalArrayResilientTests {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
struct OptionalPolymorphicArrayValueResilientTests {
struct OptionalArrayResilientTests {
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 30-30: Type name 'OptionalPolymorphicArrayValueResilientTests' should be between 3 and 40 characters long

(type_name)

🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueResilientTests.swift
at line 30, the test suite name is too long and violates SwiftLint's naming
recommendations. Rename the struct to a shorter, more concise name that still
clearly represents its purpose, reducing its length to comply with SwiftLint
guidelines.

@ElonPark ElonPark force-pushed the feature/elon/add-resilient-decoding branch from 38097d1 to c31eebe Compare July 31, 2025 09:37
Copy link
Contributor

@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: 3

♻️ Duplicate comments (12)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (2)

23-36: Equatable implementation could benefit from documentation [MEDIUM]

The Equatable implementation ignores the actual error value in the recoveredFrom case, comparing only the wasReported flag. While this is a reasonable design choice, it should be documented to prevent confusion.


41-47: Clean up unused static property [LOW]

The static let recoveredFromDebugOnlyError = Self() on line 45 is never referenced—recoveredFrom(_:wasReported:) always returns a fresh Self(). Consider removing this unused property to eliminate dead code.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1)

47-75: Fallback behavior outcome semantics worth reconsidering [MEDIUM]

The test expects outcome == .decodedSuccessfully even when an unknown type triggers fallback behavior. Should this scenario use a different outcome like .recoveredFrom() to distinguish between perfect decoding and fallback decoding? This would provide more granular debugging information.

Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)

30-41: Assertion safety concern remains unaddressed [HIGH]

The assert(!wasReported) at line 36 will crash in debug builds if the wasReported flag ever becomes true. Consider replacing with documented invariant checking or graceful fallback handling to prevent unexpected debug-time failures.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (2)

74-95: Error reporting test needs DEBUG guard investigation [HIGH]

Past reviews indicated this test fails because error reporting occurs in all builds, not just DEBUG. The test expects errors to be collected and reported, but if reportError(_:) calls aren't properly guarded with #if DEBUG, release builds will also report errors when the test expects nil.


150-158: Useful utility extension worth sharing [LOW]

The isFailure computed property on Result improves test readability. Consider moving this to a shared test utilities file if other test suites could benefit from it.

Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (2)

12-28: Consider memory optimization for large arrays [MEDIUM]

The ArrayDecodingError struct stores all decoding results, including successes. For large arrays, this could consume significant memory even in DEBUG builds. Consider storing only failures or limiting the number of stored results.


35-37: Replace assert with proper error handling [HIGH]

The assert(!wasReported) on line 36 could crash the app in DEBUG builds. Consider handling this case more gracefully or documenting why this condition should never occur.

Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (2)

65-65: Fix incorrect comment referencing Base64 [LOW]

The comment mentions "Invalid Base64 format" but this test is about date parsing, not Base64 conversion.


89-89: Fix incorrect comment referencing Data instead of Date [LOW]

The comment says "null values cannot be converted to Data" but should say "null values cannot be converted to Date".

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1)

55-55: Clarify misleading comment about Int dictionary keys [LOW]

The comment "All Int keys are valid" is misleading because the test actually ignores non-integer keys like "invalid". Update to clarify that non-integer keys are filtered out.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)

33-45: Thread safety concern for error reporter replacement [MEDIUM]

The method modifies the existing reporter's state without synchronization, which could lead to race conditions if the decoder is used concurrently.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38097d1 and c31eebe.

📒 Files selected for processing (12)
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1 hunks)
  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
Tests/KarrotCodableKitTests/**

⚙️ CodeRabbit Configuration File

Tests/KarrotCodableKitTests/**: You are a senior Swift/iOS engineer reviewing runtime functionality tests for KarrotCodableKit's Codable extensions.

1. Runtime Behavior Testing [HIGH]

  • Verify JSON encoding/decoding scenarios cover real-world use cases
  • Check polymorphic type resolution testing across different strategies
  • Assess property wrapper testing (@DATEvalue, @DefaultFalse, etc.)
  • Validate error handling and edge case coverage

2. Test Data Quality [HIGH]

  • Review test JSON structures for realistic complexity
  • Check that TestDoubles provide comprehensive mock scenarios
  • Verify test data covers various data types and edge cases
  • Assess polymorphic test data represents actual usage patterns

3. Integration Testing [MEDIUM]

  • Check integration between macro-generated code and runtime functionality
  • Verify end-to-end scenarios combining multiple KarrotCodableKit features
  • Assess performance testing for large data structures
  • Review memory usage testing for complex polymorphic scenarios

Review Focus

  • Prioritize real-world usage scenario coverage
  • Focus on Codable correctness and performance implications
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
Sources/KarrotCodableKit/**

⚙️ CodeRabbit Configuration File

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift
  • Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift
  • Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift
🧬 Code Graph Analysis (2)
Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • errors (111-120)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (46-46)
Sources/KarrotCodableKit/Resilient/ArrayDecodingError.swift (1)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (46-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build and Test (16.1, release)
  • GitHub Check: Check Macro Compatibility
🔇 Additional comments (23)
Tests/KarrotCodableKitTests/BetterCodable/DataValue/DataValueResilientTests.swift (4)

12-17: Well-structured test fixture for Base64 data scenarios [HIGH]

The fixture design with multiple @DataValue<Base64Strategy> properties effectively tests the property wrapper's behavior across different data inputs. This mirrors real-world usage where multiple Base64-encoded fields might exist in a single payload.


19-41: Comprehensive success case validation [HIGH]

The test properly validates both the decoded data content and the projected value's outcome tracking. The debug-only assertions for $base64Data.outcome provide excellent introspection capabilities without impacting release builds.


43-64: Robust invalid input handling test [HIGH]

Testing with malformed Base64 strings ensures the property wrapper correctly fails fast rather than silently producing corrupt data. The use of confirmation for async error validation is appropriate for verifying exception behavior.


89-117: Resilient error reporting verified

I’ve confirmed that every decoding failure in @DataValue is reported before it’s rethrown, and that JSONDecoder.enableResilientDecodingErrorReporting() properly registers the reporter:

  • DataValue initializer reports errors:
    • Sources/KarrotCodableKit/BetterCodable/DataValue/DataValue.swift lines 53–58
  • JSONDecoder extension registers the reporter:
    • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift lines 54–56

The test in DataValueResilientTests.swift will therefore capture the type-mismatch error as intended. No further action needed.

Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)

10-21: Excellent conditional compilation strategy [HIGH]

The debug-only enum provides rich error tracking during development while the release struct maintains minimal memory footprint. This design strikes the perfect balance between debugging capability and production performance.

Tests/KarrotCodableKitTests/PolymorphicCodable/Value/PolymorphicValueResilientTests.swift (2)

14-16: Realistic polymorphic test fixture [HIGH]

Using any DummyNotice with the @DummyNotice.Polymorphic wrapper effectively tests real-world polymorphic decoding scenarios where the concrete type is determined at runtime based on a discriminator field.


101-138: Comprehensive error reporting validation [HIGH]

The test properly exercises error reporting integration with complex polymorphic decoding failures. Using type mismatches in nested properties (like "key": 123 instead of string) provides realistic error scenarios that would occur in production.

Sources/KarrotCodableKit/Resilient/DictionaryDecodingError.swift (1)

10-28: Well-designed error aggregation structure [HIGH]

The DictionaryDecodingError struct effectively aggregates per-key decoding results, with the errors computed property providing clean access to only the failed keys. This design supports detailed debugging without cluttering successful cases.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (2)

12-23: Excellent multi-type test fixture design [HIGH]

The fixture with integers, strings, and NestedObject arrays provides comprehensive coverage of different decoding failure modes. The nested object with multiple properties adds realistic complexity that mirrors production JSON structures.


25-72: Thorough element-level error tracking validation [HIGH]

The test excellently validates both the successful filtering behavior and the detailed error tracking per array element. The assertions checking results.count vs errors.count and individual element success/failure states provide comprehensive verification of the lossy decoding mechanism.

Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessValueResilientTests.swift (1)

12-133: Well-structured and comprehensive test suite! [HIGH]

The test coverage for LosslessValue resilient decoding is excellent:

  • Proper fixture design with multiple primitive types
  • Good coverage of edge cases (null values, unconvertible types)
  • Correct use of DEBUG compilation for projected value testing
  • Clear test names and appropriate use of the confirmation pattern
Tests/KarrotCodableKitTests/BetterCodable/LosslessValue/LosslessArrayResilientTests.swift (2)

95-95: Print statement already properly guarded [LOW]

The debug print statement is already wrapped in #if DEBUG, so it won't produce output in release builds or CI/CD pipelines.


71-97: Error reporting test correctly handles all build configurations [HIGH]

The test properly verifies error reporting functionality without build-specific conditionals. The assertion that errors are reported (line 93) is correct for both DEBUG and release builds.

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (5)

12-21: Well-structured test fixture covering diverse default value strategies [HIGH]

The Fixture struct effectively tests multiple @Default* property wrappers in a single test model, providing good coverage for different data types.


22-63: Comprehensive test for error reporting through projected values [HIGH]

Excellent test that validates both the resilient behavior (falling back to defaults) and the error capture mechanism. The type mismatch validation adds good specificity to the error checking.


215-237: Well-designed test enums for frozen vs non-frozen behavior [HIGH]

Excellent separation of concerns between regular and frozen enums. The isFrozen flag implementation clearly demonstrates the different error handling behaviors.


266-298: Excellent validation of non-frozen enum error handling [HIGH]

The test properly validates that non-frozen enums generate UnknownNovelValueError and correctly extracts the novel value. Great use of pattern matching for error inspection.


300-337: Thorough testing of frozen enum error reporting [HIGH]

Excellent validation that frozen enums generate DecodingError.dataCorrupted and report to the error reporter. The integration with the error reporting system is well-tested.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (5)

58-73: Well-implemented decode method with error reporting [HIGH]

The method properly manages the error reporter lifecycle by saving and restoring the previous reporter. This prevents leaking state between decode operations.


78-103: Clean error reporter implementation with proper state management [HIGH]

The flush pattern ensures errors are properly collected and the state is reset between operations. Good encapsulation of the error collection logic.


151-170: Robust error reporting extension with proper defensive checks [HIGH]

The implementation properly handles edge cases like missing reporter and type mismatches. Good use of the coding path for error localization.


176-193: Excellent debug visualization for error hierarchy [MEDIUM]

The recursive tree structure with proper indentation makes it easy to understand the error hierarchy during debugging. The sorted output ensures consistent results.


230-241: Well-designed UnknownNovelValueError for graceful enum evolution [HIGH]

This error type elegantly handles the common case of new enum values in evolving APIs. The ability to suppress reporting while still tracking these errors is a thoughtful design choice.

Comment on lines +9 to +10
// Created by George Leontiev on 3/25/20.
// Copyright © 2020 Airbnb Inc.
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Inconsistent copyright attribution [LOW]

The file header indicates it was created by Elon for KarrotCodableKit, but line 9-10 references George Leontiev and Airbnb Inc. This should be clarified or properly attributed.

🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/Resilient/ErrorReporting.swift at lines 9 to 10, the
copyright header incorrectly attributes the file to George Leontiev and Airbnb
Inc., while the file was created by Elon for KarrotCodableKit. Update the
copyright attribution to correctly reflect the original creator and organization
associated with this file, ensuring consistency and proper credit.

Comment on lines 107 to 120
public var errors: [Error] {
errors(includeUnknownNovelValueErrors: false)
}

public func errors(includeUnknownNovelValueErrors: Bool) -> [Error] {
let allErrors: [Error] =
if mayBeMissingReportedErrors {
[MayBeMissingReportedErrors()] + root.errors
} else {
root.errors
}

return allErrors.filter { includeUnknownNovelValueErrors || !($0 is UnknownNovelValueError) }
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider caching filtered errors for performance [LOW]

The errors property filters the error list on each access. For large error trees, consider caching the filtered result.

 public var errors: [Error] {
   errors(includeUnknownNovelValueErrors: false)
 }
+
+private var cachedErrors: [Error]?
+private var cachedIncludeUnknown: Bool?

 public func errors(includeUnknownNovelValueErrors: Bool) -> [Error] {
+  if let cached = cachedErrors, cachedIncludeUnknown == includeUnknownNovelValueErrors {
+    return cached
+  }
+  
   let allErrors: [Error] =
     if mayBeMissingReportedErrors {
       [MayBeMissingReportedErrors()] + root.errors
     } else {
       root.errors
     }
 
-  return allErrors.filter { includeUnknownNovelValueErrors || !($0 is UnknownNovelValueError) }
+  let filtered = allErrors.filter { includeUnknownNovelValueErrors || !($0 is UnknownNovelValueError) }
+  cachedErrors = filtered
+  cachedIncludeUnknown = includeUnknownNovelValueErrors
+  return filtered
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/Resilient/ErrorReporting.swift around lines 107 to
120, the errors property filters the error list every time it is accessed, which
can be inefficient for large error trees. To improve performance, introduce a
caching mechanism to store the filtered errors result. Update the cache only
when the underlying error data changes, and return the cached filtered errors on
subsequent accesses to avoid repeated filtering.

Comment on lines +172 to +180
let errorDigest = errorReporter.flushReportedErrors()

let digest = try #require(errorDigest)
// At least 3 errors should be reported
#expect(digest.errors.count >= 3)
#if DEBUG
print("Error digest: \(digest.debugDescription)")
#endif
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider validating specific error types in error digest [MEDIUM]

While checking the error count is good, consider also validating the specific error types in the digest to ensure the correct errors are being reported.

 let digest = try #require(errorDigest)
 // At least 3 errors should be reported
 #expect(digest.errors.count >= 3)
+
+// Verify specific error types
+let errors = digest.errors
+let typeErrors = errors.compactMap { $0 as? DecodingError }
+#expect(typeErrors.count >= 3)
+
 #if DEBUG
 print("Error digest: \(digest.debugDescription)")
 #endif
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let errorDigest = errorReporter.flushReportedErrors()
let digest = try #require(errorDigest)
// At least 3 errors should be reported
#expect(digest.errors.count >= 3)
#if DEBUG
print("Error digest: \(digest.debugDescription)")
#endif
}
let errorDigest = errorReporter.flushReportedErrors()
let digest = try #require(errorDigest)
// At least 3 errors should be reported
#expect(digest.errors.count >= 3)
// Verify specific error types
let errors = digest.errors
let typeErrors = errors.compactMap { $0 as? DecodingError }
#expect(typeErrors.count >= 3)
#if DEBUG
print("Error digest: \(digest.debugDescription)")
#endif
🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
around lines 172 to 180, the test currently only checks that at least three
errors are reported but does not verify the specific error types. Enhance the
test by iterating through the errors in the digest and asserting that each error
matches the expected error types to ensure the correct errors are being
reported.

Copy link
Contributor

@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: 4

♻️ Duplicate comments (5)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (2)

69-74: Incorrect outcome determination logic

The condition will always evaluate to true since both arrays are incremented together in the loop. This results in incorrect outcome reporting when decoding errors occur.


61-73: Inconsistent error reporting flag

The outcome is created with wasReported: false despite errors being reported via decoder.reportError(error).

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1)

25-78: Update misleading comment about Int dictionary keys [LOW]

The comment "All Int keys are valid" on line 55 is misleading since non-integer keys like "invalid" are silently ignored. Please update it to clarify this behavior.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)

1-18: Clarify copyright attribution [LOW]

The file header indicates creation by Elon for KarrotCodableKit, but lines 9-10 reference George Leontiev and Airbnb Inc. This inconsistency should be clarified.


20-46: Document thread safety requirements [MEDIUM]

The replaceResilientDecodingErrorReporter method modifies state without synchronization. Since JSONDecoder's userInfo isn't thread-safe, document that decoders with error reporters must not be shared across threads.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c31eebe and 6ea6681.

📒 Files selected for processing (10)
  • Sources/KarrotCodableKit/BetterCodable/Extenstoins/Result+Extension.swift (1 hunks)
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift (3 hunks)
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
Sources/KarrotCodableKit/**

⚙️ CodeRabbit Configuration File

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/BetterCodable/Extenstoins/Result+Extension.swift
  • Sources/KarrotCodableKit/BetterCodable/LosslessValue/LosslessArray.swift
  • Sources/KarrotCodableKit/Resilient/ErrorReporting.swift
Tests/KarrotCodableKitTests/**

⚙️ CodeRabbit Configuration File

Tests/KarrotCodableKitTests/**: You are a senior Swift/iOS engineer reviewing runtime functionality tests for KarrotCodableKit's Codable extensions.

1. Runtime Behavior Testing [HIGH]

  • Verify JSON encoding/decoding scenarios cover real-world use cases
  • Check polymorphic type resolution testing across different strategies
  • Assess property wrapper testing (@DATEvalue, @DefaultFalse, etc.)
  • Validate error handling and edge case coverage

2. Test Data Quality [HIGH]

  • Review test JSON structures for realistic complexity
  • Check that TestDoubles provide comprehensive mock scenarios
  • Verify test data covers various data types and edge cases
  • Assess polymorphic test data represents actual usage patterns

3. Integration Testing [MEDIUM]

  • Check integration between macro-generated code and runtime functionality
  • Verify end-to-end scenarios combining multiple KarrotCodableKit features
  • Assess performance testing for large data structures
  • Review memory usage testing for complex polymorphic scenarios

Review Focus

  • Prioritize real-world usage scenario coverage
  • Focus on Codable correctness and performance implications
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift
🔇 Additional comments (20)
Sources/KarrotCodableKit/BetterCodable/Extenstoins/Result+Extension.swift (1)

10-34: Well-implemented Result convenience extensions

The extension provides clean and efficient accessors for Result types. The use of pattern matching and optional chaining is appropriate, and the package visibility aligns with the framework's internal usage patterns.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicArrayValueResilientTests.swift (1)

1-161: Comprehensive and well-structured test suite

Excellent test coverage for PolymorphicArrayValue resilient decoding. The tests properly validate both functional behavior and debug-only outcome tracking, with clear scenarios covering edge cases and error conditions.

Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueResilientTests.swift (1)

1-117: Well-designed test suite for optional polymorphic values

The test suite effectively covers all decoding scenarios for optional polymorphic values, including proper validation of outcome states and error conditions. Good use of the Testing framework features.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyArrayResilientTests.swift (1)

1-148: Thorough test coverage for LossyArray resilient decoding

Excellent test suite that validates both the lossy decoding behavior and error tracking capabilities. The tests properly verify partial decoding success, error reporting, and projected value access in debug builds. Good use of structured test data with nested objects.

Tests/KarrotCodableKitTests/BetterCodable/DateValue/DateValueResilientTests.swift (4)

20-45: Well-structured test for successful decoding scenarios

The test properly validates successful date decoding with different strategies and correctly checks projected values in DEBUG mode.


47-69: Good error handling test coverage

The test appropriately verifies that invalid date formats cause decoding failures using the confirmation pattern.


71-93: Proper null value handling validation

The test correctly verifies that null values in date fields cause decoding failures.


95-125: Comprehensive error reporting integration test

The test effectively validates that type mismatches are properly reported through the resilient decoding error reporter.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/PolymorphicLossyArrayValueResilientTests.swift (6)

11-32: Good edge case coverage for empty arrays

The test properly validates that empty arrays decode successfully with appropriate outcome tracking.


33-71: Excellent test for successful polymorphic array decoding

The test thoroughly validates successful decoding with proper type checking and uses the Result extension effectively for clean assertions.


72-122: Comprehensive lossy array behavior validation

The test excellently covers various invalid element scenarios and properly validates that only valid elements are retained while tracking individual element outcomes.


123-142: Proper missing key handling

The test correctly validates that missing array keys result in empty arrays with appropriate outcome tracking.


143-168: Good type mismatch validation

The test properly validates that non-array types result in empty arrays with recoveredFrom outcome and error tracking.


169-203: Effective error reporter integration test

The test properly validates that partial decoding failures are captured by the error reporter while still decoding valid elements.

Tests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyDictionaryResilientTests.swift (3)

80-106: Good error reporting coverage

The test effectively validates that type mismatches in dictionary values are properly reported through the error reporter.


107-133: Comprehensive failure scenario testing

The test thoroughly validates complete decoding failures with proper distinction between type mismatches and null values.


134-154: Proper missing key validation

The test correctly validates that missing dictionary keys result in empty dictionaries without errors.

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (1)

215-421: Excellent RawRepresentable test coverage

The RawRepresentable tests comprehensively cover both frozen and non-frozen enum scenarios with proper distinction between UnknownNovelValueError and DecodingError behaviors. The test structure clearly validates the different handling strategies.

Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (2)

78-103: Well-designed error reporter implementation

The ResilientDecodingErrorReporter class efficiently collects errors with a clean API and good optimization using the hasErrors flag.


148-241: Excellent error reporting integration and debug support

The Decoder extension provides clean error reporting integration, and the debug-only pretty printing with abridged error descriptions is very helpful for development. The UnknownNovelValueError design provides good flexibility for suppressing expected novel values.

@@ -0,0 +1,34 @@
//
// ResultExtenstions.swift
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Fix typo in filename [MEDIUM]

The filename has a typo: "ResultExtenstions.swift" should be "ResultExtensions.swift".

🤖 Prompt for AI Agents
In Sources/KarrotCodableKit/BetterCodable/Extenstoins/Result+Extension.swift at
line 2, fix the typo in the filename from "ResultExtenstions.swift" to
"ResultExtensions.swift" by renaming the file accordingly to ensure correct
spelling.

Comment on lines +22 to +63
@Test("projected value provides error information for failed decoding")
func testProjectedValueProvidesErrorInfo() throws {
let json = """
{
"intValue": "not a number",
"stringValue": 123,
"boolValue": "not a bool",
"arrayValue": "not an array",
"dictValue": "not a dict"
}
"""

let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)

// Verify default behavior - use default value on decoding failure
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])

#if DEBUG
// Access error info through projected value
#expect(fixture.$intValue.error != nil)
#expect(fixture.$stringValue.error != nil)
#expect(fixture.$boolValue.error != nil)
#expect(fixture.$arrayValue.error != nil)
#expect(fixture.$dictValue.error != nil)

// Check error type
let error = try #require(fixture.$intValue.error as? DecodingError)
switch error {
case .typeMismatch:
// Expected
break
default:
Issue.record("Expected typeMismatch error")
}
#endif
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use #require instead of force unwrapping [MEDIUM]

Replace force unwrapping with #require for safer data conversion, consistent with other tests in this PR:

-    let data = json.data(using: .utf8)!
+    let data = try #require(json.data(using: .utf8))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test("projected value provides error information for failed decoding")
func testProjectedValueProvidesErrorInfo() throws {
let json = """
{
"intValue": "not a number",
"stringValue": 123,
"boolValue": "not a bool",
"arrayValue": "not an array",
"dictValue": "not a dict"
}
"""
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)
// Verify default behavior - use default value on decoding failure
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])
#if DEBUG
// Access error info through projected value
#expect(fixture.$intValue.error != nil)
#expect(fixture.$stringValue.error != nil)
#expect(fixture.$boolValue.error != nil)
#expect(fixture.$arrayValue.error != nil)
#expect(fixture.$dictValue.error != nil)
// Check error type
let error = try #require(fixture.$intValue.error as? DecodingError)
switch error {
case .typeMismatch:
// Expected
break
default:
Issue.record("Expected typeMismatch error")
}
#endif
}
@Test("projected value provides error information for failed decoding")
func testProjectedValueProvidesErrorInfo() throws {
let json = """
{
"intValue": "not a number",
"stringValue": 123,
"boolValue": "not a bool",
"arrayValue": "not an array",
"dictValue": "not a dict"
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(Fixture.self, from: data)
// Verify default behavior - use default value on decoding failure
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])
#if DEBUG
// Access error info through projected value
#expect(fixture.$intValue.error != nil)
#expect(fixture.$stringValue.error != nil)
#expect(fixture.$boolValue.error != nil)
#expect(fixture.$arrayValue.error != nil)
#expect(fixture.$dictValue.error != nil)
// Check error type
let error = try #require(fixture.$intValue.error as? DecodingError)
switch error {
case .typeMismatch:
// Expected
break
default:
Issue.record("Expected typeMismatch error")
}
#endif
}
🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
between lines 22 and 63, replace the force unwrapping of the optional returned
by json.data(using: .utf8) with the #require function to safely unwrap the data.
This change ensures safer data conversion and maintains consistency with other
tests in the PR by avoiding potential runtime crashes from force unwrapping.

Comment on lines +65 to +89
@Test("missing keys use default values without error")
func testMissingKeysUseDefaultValues() throws {
let json = "{}"

let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)

// Check default values
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])

#if DEBUG
// No error when key is missing (default behavior)
#expect(fixture.$intValue.error == nil)
#expect(fixture.$stringValue.error == nil)
#expect(fixture.$boolValue.error == nil)
#expect(fixture.$arrayValue.error == nil)
#expect(fixture.$dictValue.error == nil)
#endif
}

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use #require for data conversion [MEDIUM]

Replace force unwrapping for consistency:

-    let data = json.data(using: .utf8)!
+    let data = try #require(json.data(using: .utf8))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test("missing keys use default values without error")
func testMissingKeysUseDefaultValues() throws {
let json = "{}"
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)
// Check default values
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])
#if DEBUG
// No error when key is missing (default behavior)
#expect(fixture.$intValue.error == nil)
#expect(fixture.$stringValue.error == nil)
#expect(fixture.$boolValue.error == nil)
#expect(fixture.$arrayValue.error == nil)
#expect(fixture.$dictValue.error == nil)
#endif
}
@Test("missing keys use default values without error")
func testMissingKeysUseDefaultValues() throws {
let json = "{}"
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(Fixture.self, from: data)
// Check default values
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])
#if DEBUG
// No error when key is missing (default behavior)
#expect(fixture.$intValue.error == nil)
#expect(fixture.$stringValue.error == nil)
#expect(fixture.$boolValue.error == nil)
#expect(fixture.$arrayValue.error == nil)
#expect(fixture.$dictValue.error == nil)
#endif
}
🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
between lines 65 and 89, the JSON data conversion uses force unwrapping with
'json.data(using: .utf8)!'. Replace this force unwrap with '#require' to safely
unwrap the optional and maintain consistency in error handling. Change the line
to use '#require' to unwrap the data conversion result instead of force
unwrapping.

Comment on lines +90 to +421
@Test("valid values decode successfully")
func testValidValuesDecodeSuccessfully() throws {
let json = """
{
"intValue": 42,
"stringValue": "hello",
"boolValue": true,
"arrayValue": ["a", "b", "c"],
"dictValue": {"key": 123}
}
"""

let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)

// Check normal values
#expect(fixture.intValue == 42)
#expect(fixture.stringValue == "hello")
#expect(fixture.boolValue == true)
#expect(fixture.arrayValue == ["a", "b", "c"])
#expect(fixture.dictValue == ["key": 123])

#if DEBUG
// No error when successfully decoded
#expect(fixture.$intValue.error == nil)
#expect(fixture.$stringValue.error == nil)
#expect(fixture.$boolValue.error == nil)
#expect(fixture.$arrayValue.error == nil)
#expect(fixture.$dictValue.error == nil)
#endif
}

@Test("null values use default values")
func testNullValuesUseDefaultValues() throws {
let json = """
{
"intValue": null,
"stringValue": null,
"boolValue": null,
"arrayValue": null,
"dictValue": null
}
"""

let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)

// Use default value for null
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])

#if DEBUG
// null is not considered an error
#expect(fixture.$intValue.error == nil)
#expect(fixture.$stringValue.error == nil)
#expect(fixture.$boolValue.error == nil)
#expect(fixture.$arrayValue.error == nil)
#expect(fixture.$dictValue.error == nil)
#endif
}

@Test("error reporting with JSONDecoder")
func testErrorReportingWithDecoder() throws {
let json = """
{
"intValue": "invalid",
"stringValue": [],
"boolValue": {}
}
"""

let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()

let data = json.data(using: .utf8)!
_ = try decoder.decode(Fixture.self, from: data)

let errorDigest = errorReporter.flushReportedErrors()

let digest = try #require(errorDigest)
// At least 3 errors should be reported
#expect(digest.errors.count >= 3)
#if DEBUG
print("Error digest: \(digest.debugDescription)")
#endif
}

@Test("LossyOptional behavior")
func testLossyOptional() throws {
struct OptionalFixture: Decodable {
@LossyOptional var url: URL?
@LossyOptional var date: Date?
@LossyOptional var number: Int?
}

let json = """
{
"url": "https://example .com",
"date": "not a date",
"number": "not a number"
}
"""

let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(OptionalFixture.self, from: data)

// nil on decoding failure
#expect(fixture.url == nil)
#expect(fixture.date == nil)
#expect(fixture.number == nil)

#if DEBUG
// Check error info
#expect(fixture.$url.error != nil)
#expect(fixture.$date.error != nil)
#expect(fixture.$number.error != nil)
#endif
}

// MARK: - RawRepresentable Support Tests

enum TestEnum: String, Decodable, DefaultCodableStrategy {
case first
case second
case unknown

static var defaultValue: TestEnum { .unknown }
}

enum FrozenTestEnum: String, Decodable, DefaultCodableStrategy {
case alpha
case beta
case fallback

static var defaultValue: FrozenTestEnum { .fallback }
static var isFrozen: Bool { true }
}

struct RawRepresentableFixture: Decodable {
@DefaultCodable<TestEnum> var normalEnum: TestEnum
@DefaultCodable<FrozenTestEnum> var frozenEnum: FrozenTestEnum
}

@Test("RawRepresentable with valid raw values")
func testRawRepresentableValidValues() throws {
// given
let json = """
{
"normalEnum": "first",
"frozenEnum": "alpha"
}
"""

// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)

// then
#expect(fixture.normalEnum == .first)
#expect(fixture.frozenEnum == .alpha)

#if DEBUG
#expect(fixture.$normalEnum.outcome == .decodedSuccessfully)
#expect(fixture.$frozenEnum.outcome == .decodedSuccessfully)
#expect(fixture.$normalEnum.error == nil)
#expect(fixture.$frozenEnum.error == nil)
#endif
}

@Test("RawRepresentable with unknown raw values (non-frozen)")
func testRawRepresentableUnknownValueNonFrozen() throws {
// given
let json = """
{
"normalEnum": "third",
"frozenEnum": "beta"
}
"""

// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)

// then
#expect(fixture.normalEnum == .unknown) // Should use default value
#expect(fixture.frozenEnum == .beta)

#if DEBUG
// Non-frozen enum should recover with UnknownNovelValueError
if case .recoveredFrom(let error, _) = fixture.$normalEnum.outcome {
#expect(error is UnknownNovelValueError)
if let unknownError = error as? UnknownNovelValueError {
#expect(unknownError.novelValue as? String == "third")
}
} else {
Issue.record("Expected recoveredFrom outcome for non-frozen enum")
}

#expect(fixture.$frozenEnum.outcome == .decodedSuccessfully)
#endif
}

@Test("RawRepresentable with unknown raw values (frozen)")
func testRawRepresentableUnknownValueFrozen() throws {
// given
let json = """
{
"normalEnum": "first",
"frozenEnum": "gamma"
}
"""

// when
let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)

// then
#expect(fixture.normalEnum == .first)
#expect(fixture.frozenEnum == .fallback) // Should use default value due to error

#if DEBUG
// Frozen enum should report DecodingError
if case .recoveredFrom(let error, _) = fixture.$frozenEnum.outcome {
#expect(error is DecodingError)
if case .dataCorrupted = error as? DecodingError {
// Expected
} else {
Issue.record("Expected dataCorrupted DecodingError for frozen enum")
}
} else {
Issue.record("Expected recoveredFrom outcome for frozen enum")
}

// Error should be reported to error reporter
let errorDigest = errorReporter.flushReportedErrors()
#expect(errorDigest != nil)
#endif
}

@Test("RawRepresentable with missing keys")
func testRawRepresentableMissingKeys() throws {
// given
let json = "{}"

// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)

// then
#expect(fixture.normalEnum == .unknown)
#expect(fixture.frozenEnum == .fallback)

#if DEBUG
#expect(fixture.$normalEnum.outcome == .keyNotFound)
#expect(fixture.$frozenEnum.outcome == .keyNotFound)
#expect(fixture.$normalEnum.error == nil)
#expect(fixture.$frozenEnum.error == nil)
#endif
}

@Test("RawRepresentable with null values")
func testRawRepresentableNullValues() throws {
// given
let json = """
{
"normalEnum": null,
"frozenEnum": null
}
"""

// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)

// then
#expect(fixture.normalEnum == .unknown)
#expect(fixture.frozenEnum == .fallback)

#if DEBUG
#expect(fixture.$normalEnum.outcome == .valueWasNil)
#expect(fixture.$frozenEnum.outcome == .valueWasNil)
#expect(fixture.$normalEnum.error == nil)
#expect(fixture.$frozenEnum.error == nil)
#endif
}

@Test("RawRepresentable with type mismatch")
func testRawRepresentableTypeMismatch() throws {
// given - enums expect String but we provide numbers
let json = """
{
"normalEnum": 123,
"frozenEnum": true
}
"""

// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)

// then
#expect(fixture.normalEnum == .unknown)
#expect(fixture.frozenEnum == .fallback)

#if DEBUG
// Both should have type mismatch errors
if case .recoveredFrom(let error, _) = fixture.$normalEnum.outcome {
#expect(error is DecodingError)
if case .typeMismatch = error as? DecodingError {
// Expected
} else {
Issue.record("Expected typeMismatch DecodingError")
}
} else {
Issue.record("Expected recoveredFrom outcome")
}
#endif
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consistently use #require throughout the test file [MEDIUM]

Multiple test methods use force unwrapping for data conversion. For consistency with other test files in this PR and better error handling, replace all instances:

-    let data = json.data(using: .utf8)!
+    let data = try #require(json.data(using: .utf8))

This applies to lines: 103, 136, 169, 199, 251, 278, 313, 346, 373, 400

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test("valid values decode successfully")
func testValidValuesDecodeSuccessfully() throws {
let json = """
{
"intValue": 42,
"stringValue": "hello",
"boolValue": true,
"arrayValue": ["a", "b", "c"],
"dictValue": {"key": 123}
}
"""
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)
// Check normal values
#expect(fixture.intValue == 42)
#expect(fixture.stringValue == "hello")
#expect(fixture.boolValue == true)
#expect(fixture.arrayValue == ["a", "b", "c"])
#expect(fixture.dictValue == ["key": 123])
#if DEBUG
// No error when successfully decoded
#expect(fixture.$intValue.error == nil)
#expect(fixture.$stringValue.error == nil)
#expect(fixture.$boolValue.error == nil)
#expect(fixture.$arrayValue.error == nil)
#expect(fixture.$dictValue.error == nil)
#endif
}
@Test("null values use default values")
func testNullValuesUseDefaultValues() throws {
let json = """
{
"intValue": null,
"stringValue": null,
"boolValue": null,
"arrayValue": null,
"dictValue": null
}
"""
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(Fixture.self, from: data)
// Use default value for null
#expect(fixture.intValue == 0)
#expect(fixture.stringValue == "")
#expect(fixture.boolValue == false)
#expect(fixture.arrayValue == [])
#expect(fixture.dictValue == [:])
#if DEBUG
// null is not considered an error
#expect(fixture.$intValue.error == nil)
#expect(fixture.$stringValue.error == nil)
#expect(fixture.$boolValue.error == nil)
#expect(fixture.$arrayValue.error == nil)
#expect(fixture.$dictValue.error == nil)
#endif
}
@Test("error reporting with JSONDecoder")
func testErrorReportingWithDecoder() throws {
let json = """
{
"intValue": "invalid",
"stringValue": [],
"boolValue": {}
}
"""
let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()
let data = json.data(using: .utf8)!
_ = try decoder.decode(Fixture.self, from: data)
let errorDigest = errorReporter.flushReportedErrors()
let digest = try #require(errorDigest)
// At least 3 errors should be reported
#expect(digest.errors.count >= 3)
#if DEBUG
print("Error digest: \(digest.debugDescription)")
#endif
}
@Test("LossyOptional behavior")
func testLossyOptional() throws {
struct OptionalFixture: Decodable {
@LossyOptional var url: URL?
@LossyOptional var date: Date?
@LossyOptional var number: Int?
}
let json = """
{
"url": "https://example .com",
"date": "not a date",
"number": "not a number"
}
"""
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(OptionalFixture.self, from: data)
// nil on decoding failure
#expect(fixture.url == nil)
#expect(fixture.date == nil)
#expect(fixture.number == nil)
#if DEBUG
// Check error info
#expect(fixture.$url.error != nil)
#expect(fixture.$date.error != nil)
#expect(fixture.$number.error != nil)
#endif
}
// MARK: - RawRepresentable Support Tests
enum TestEnum: String, Decodable, DefaultCodableStrategy {
case first
case second
case unknown
static var defaultValue: TestEnum { .unknown }
}
enum FrozenTestEnum: String, Decodable, DefaultCodableStrategy {
case alpha
case beta
case fallback
static var defaultValue: FrozenTestEnum { .fallback }
static var isFrozen: Bool { true }
}
struct RawRepresentableFixture: Decodable {
@DefaultCodable<TestEnum> var normalEnum: TestEnum
@DefaultCodable<FrozenTestEnum> var frozenEnum: FrozenTestEnum
}
@Test("RawRepresentable with valid raw values")
func testRawRepresentableValidValues() throws {
// given
let json = """
{
"normalEnum": "first",
"frozenEnum": "alpha"
}
"""
// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
// then
#expect(fixture.normalEnum == .first)
#expect(fixture.frozenEnum == .alpha)
#if DEBUG
#expect(fixture.$normalEnum.outcome == .decodedSuccessfully)
#expect(fixture.$frozenEnum.outcome == .decodedSuccessfully)
#expect(fixture.$normalEnum.error == nil)
#expect(fixture.$frozenEnum.error == nil)
#endif
}
@Test("RawRepresentable with unknown raw values (non-frozen)")
func testRawRepresentableUnknownValueNonFrozen() throws {
// given
let json = """
{
"normalEnum": "third",
"frozenEnum": "beta"
}
"""
// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
// then
#expect(fixture.normalEnum == .unknown) // Should use default value
#expect(fixture.frozenEnum == .beta)
#if DEBUG
// Non-frozen enum should recover with UnknownNovelValueError
if case .recoveredFrom(let error, _) = fixture.$normalEnum.outcome {
#expect(error is UnknownNovelValueError)
if let unknownError = error as? UnknownNovelValueError {
#expect(unknownError.novelValue as? String == "third")
}
} else {
Issue.record("Expected recoveredFrom outcome for non-frozen enum")
}
#expect(fixture.$frozenEnum.outcome == .decodedSuccessfully)
#endif
}
@Test("RawRepresentable with unknown raw values (frozen)")
func testRawRepresentableUnknownValueFrozen() throws {
// given
let json = """
{
"normalEnum": "first",
"frozenEnum": "gamma"
}
"""
// when
let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
// then
#expect(fixture.normalEnum == .first)
#expect(fixture.frozenEnum == .fallback) // Should use default value due to error
#if DEBUG
// Frozen enum should report DecodingError
if case .recoveredFrom(let error, _) = fixture.$frozenEnum.outcome {
#expect(error is DecodingError)
if case .dataCorrupted = error as? DecodingError {
// Expected
} else {
Issue.record("Expected dataCorrupted DecodingError for frozen enum")
}
} else {
Issue.record("Expected recoveredFrom outcome for frozen enum")
}
// Error should be reported to error reporter
let errorDigest = errorReporter.flushReportedErrors()
#expect(errorDigest != nil)
#endif
}
@Test("RawRepresentable with missing keys")
func testRawRepresentableMissingKeys() throws {
// given
let json = "{}"
// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
// then
#expect(fixture.normalEnum == .unknown)
#expect(fixture.frozenEnum == .fallback)
#if DEBUG
#expect(fixture.$normalEnum.outcome == .keyNotFound)
#expect(fixture.$frozenEnum.outcome == .keyNotFound)
#expect(fixture.$normalEnum.error == nil)
#expect(fixture.$frozenEnum.error == nil)
#endif
}
@Test("RawRepresentable with null values")
func testRawRepresentableNullValues() throws {
// given
let json = """
{
"normalEnum": null,
"frozenEnum": null
}
"""
// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
// then
#expect(fixture.normalEnum == .unknown)
#expect(fixture.frozenEnum == .fallback)
#if DEBUG
#expect(fixture.$normalEnum.outcome == .valueWasNil)
#expect(fixture.$frozenEnum.outcome == .valueWasNil)
#expect(fixture.$normalEnum.error == nil)
#expect(fixture.$frozenEnum.error == nil)
#endif
}
@Test("RawRepresentable with type mismatch")
func testRawRepresentableTypeMismatch() throws {
// given - enums expect String but we provide numbers
let json = """
{
"normalEnum": 123,
"frozenEnum": true
}
"""
// when
let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
// then
#expect(fixture.normalEnum == .unknown)
#expect(fixture.frozenEnum == .fallback)
#if DEBUG
// Both should have type mismatch errors
if case .recoveredFrom(let error, _) = fixture.$normalEnum.outcome {
#expect(error is DecodingError)
if case .typeMismatch = error as? DecodingError {
// Expected
} else {
Issue.record("Expected typeMismatch DecodingError")
}
} else {
Issue.record("Expected recoveredFrom outcome")
}
#endif
}
}
@Test("valid values decode successfully")
func testValidValuesDecodeSuccessfully() throws {
let json = """
{
"intValue": 42,
"stringValue": "hello",
"boolValue": true,
"arrayValue": ["a", "b", "c"],
"dictValue": {"key": 123}
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(Fixture.self, from: data)
// Check normal values
#expect(fixture.intValue == 42)
}
@Test("null values use default values")
func testNullValuesUseDefaultValues() throws {
let json = """
{
"intValue": null,
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(Fixture.self, from: data)
// Use default value for null
}
@Test("error reporting with JSONDecoder")
func testErrorReportingWithDecoder() throws {
let json = """
{
"intValue": "invalid",
}
"""
let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
_ = try decoder.decode(Fixture.self, from: data)
}
@Test("LossyOptional behavior")
func testLossyOptional() throws {
let json = """
{
"url": "https://example .com",
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(OptionalFixture.self, from: data)
}
// MARK: - RawRepresentable Support Tests
@Test("RawRepresentable with valid raw values")
func testRawRepresentableValidValues() throws {
let json = """
{
"normalEnum": "first",
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
}
@Test("RawRepresentable with unknown raw values (non-frozen)")
func testRawRepresentableUnknownValueNonFrozen() throws {
let json = """
{
"normalEnum": "third",
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
}
@Test("RawRepresentable with unknown raw values (frozen)")
func testRawRepresentableUnknownValueFrozen() throws {
let json = """
{
"frozenEnum": "gamma"
}
"""
let decoder = JSONDecoder()
let errorReporter = decoder.enableResilientDecodingErrorReporting()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
}
@Test("RawRepresentable with missing keys")
func testRawRepresentableMissingKeys() throws {
let json = "{}"
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
}
@Test("RawRepresentable with null values")
func testRawRepresentableNullValues() throws {
let json = """
{
"normalEnum": null,
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
}
@Test("RawRepresentable with type mismatch")
func testRawRepresentableTypeMismatch() throws {
let json = """
{
"normalEnum": 123,
}
"""
let decoder = JSONDecoder()
- let data = json.data(using: .utf8)!
+ let data = try #require(json.data(using: .utf8))
let fixture = try decoder.decode(RawRepresentableFixture.self, from: data)
}
🤖 Prompt for AI Agents
In
Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
at lines 103, 136, 169, 199, 251, 278, 313, 346, 373, and 400, replace all force
unwraps of data conversion from json.data(using: .utf8)! with the use of
#require to safely unwrap the optional Data. This change improves consistency
and error handling by ensuring that if the data conversion fails, the test will
fail gracefully with a clear error rather than crashing.

@ElonPark ElonPark closed this Jul 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants